{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "aa148213",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最佳epochs轮数: 851\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 180ms/step\n",
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step \n",
      "RMSEc (校正均方根误差): 3.867420554749767\n",
      "RMSEp (预测均方根误差): 7.0991259238529825\n",
      "Rcal (校正集相关系数): 0.998289531081215\n",
      "Rval (验证集相关系数): 0.9958241839525879\n",
      "RPD (相对预测偏差): 10.387904294731882\n",
      "Training time: 199.22150421142578 seconds\n",
      "Testing time: 0.776310920715332 seconds\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "import numpy as np\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, Conv1D, LSTM, MaxPooling1D  # 导入 MaxPooling1D\n",
    "from tensorflow.keras.layers import LSTM, Flatten  # 导入 Flatten 和 LSTM\n",
    "\n",
    "# 读取数据\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷B']]\n",
    "\n",
    "# 特征提取\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "\n",
    "# 索引对齐\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# Kennard-Stone算法实现省略，假设函数名为kennard_stone_selection\n",
    "def kennard_stone_selection(x_variables, k):\n",
    "    x_variables = np.array(x_variables)\n",
    "    original_x = x_variables\n",
    "    distance_to_average = ((x_variables - np.tile(x_variables.mean(axis=0), (x_variables.shape[0], 1))) ** 2).sum(\n",
    "        axis=1)\n",
    "    max_distance_sample_number = np.where(distance_to_average == np.max(distance_to_average))\n",
    "    max_distance_sample_number = max_distance_sample_number[0][0]\n",
    "    selected_sample_numbers = list()\n",
    "    selected_sample_numbers.append(max_distance_sample_number)\n",
    "    remaining_sample_numbers = np.arange(0, x_variables.shape[0], 1)\n",
    "    x_variables = np.delete(x_variables, selected_sample_numbers, 0)\n",
    "    remaining_sample_numbers = np.delete(remaining_sample_numbers, selected_sample_numbers, 0)\n",
    "    for iteration in range(1, k):\n",
    "        selected_samples = original_x[selected_sample_numbers, :]\n",
    "        min_distance_to_selected_samples = list()\n",
    "        for min_distance_calculation_number in range(0, x_variables.shape[0]):\n",
    "            distance_to_selected_samples = ((selected_samples - np.tile(x_variables[min_distance_calculation_number, :],\n",
    "                                                                        (selected_samples.shape[0], 1))) ** 2).sum(\n",
    "                axis=1)\n",
    "            min_distance_to_selected_samples.append(np.min(distance_to_selected_samples))\n",
    "        max_distance_sample_number = np.where(\n",
    "            min_distance_to_selected_samples == np.max(min_distance_to_selected_samples))\n",
    "        max_distance_sample_number = max_distance_sample_number[0][0]\n",
    "        selected_sample_numbers.append(remaining_sample_numbers[max_distance_sample_number])\n",
    "        x_variables = np.delete(x_variables, max_distance_sample_number, 0)\n",
    "        remaining_sample_numbers = np.delete(remaining_sample_numbers, max_distance_sample_number, 0)\n",
    "\n",
    "    return selected_sample_numbers, remaining_sample_numbers\n",
    "\n",
    "\n",
    "# 划分数据集\n",
    "train_indices, test_indices = kennard_stone_selection(pca_features_df.values, 336)\n",
    "X_train, X_test = pca_features_df.iloc[train_indices], pca_features_df.iloc[test_indices]\n",
    "y_train, y_test = targets.iloc[train_indices], targets.iloc[test_indices]\n",
    "\n",
    "# 调整数据形状\n",
    "X_train = np.expand_dims(X_train, axis=2)\n",
    "X_test = np.expand_dims(X_test, axis=2)\n",
    "\n",
    "# 构建LSTM模型\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(128, input_shape=(1, 10), return_sequences=True))  # 修正 input_shape\n",
    "    model.add(LSTM(128))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=1e-3), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "\n",
    "# 调整数据形状以匹配LSTM输入要求\n",
    "# LSTM需要三维输入，因此需要添加一个时间步长维度\n",
    "X_train = np.expand_dims(X_train, axis=1)  # 现在形状是(samples, 1, features)\n",
    "X_test = np.expand_dims(X_test, axis=1)    # 现在形状是(samples, 1, features)\n",
    "\n",
    "# 创建LSTM模型\n",
    "model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "\n",
    "start_train_time = time.time()\n",
    "early_stopping = EarlyStopping(monitor='val_loss', patience=100)\n",
    "history = model_lstm.fit(X_train, y_train, epochs=1000, batch_size=10, validation_split=0.2, verbose=0, callbacks=[early_stopping])\n",
    "\n",
    "\n",
    "# 获取最佳epochs轮数\n",
    "best_epoch = early_stopping.stopped_epoch + 1  # +1 因为stopped_epoch是从0开始的\n",
    "print(f\"最佳epochs轮数: {best_epoch}\")\n",
    "\n",
    "end_train_time = time.time()\n",
    "train_time = end_train_time - start_train_time\n",
    "\n",
    "# 测试模型\n",
    "start_test_time = time.time()\n",
    "y_pred_lstm = model_lstm.predict(X_test)\n",
    "end_test_time = time.time()\n",
    "test_time = end_test_time - start_test_time\n",
    "\n",
    "# 计算性能指标\n",
    "def calculate_metrics(y_true, y_pred):\n",
    "    rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "    r = pearsonr(y_true.ravel(), y_pred.ravel())[0]\n",
    "    return rmse, r\n",
    "\n",
    "rmsec, r_cal = calculate_metrics(y_train.values, model_lstm.predict(X_train))\n",
    "rmsep, r_val = calculate_metrics(y_test.values, y_pred_lstm)\n",
    "RPD = np.std(y_test.values) / rmsep\n",
    "\n",
    "# 输出性能指标\n",
    "print(f\"RMSEc (校正均方根误差): {rmsec}\\nRMSEp (预测均方根误差): {rmsep}\\nRcal (校正集相关系数): {r_cal}\\nRval (验证集相关系数): {r_val}\\nRPD (相对预测偏差): {RPD}\")\n",
    "print(f\"Training time: {train_time} seconds\")\n",
    "print(f\"Testing time: {test_time} seconds\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "dc19475b",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X_train_scaled shape: (336, 1, 10)\n",
      "X_test_scaled shape: (67, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最佳训练轮数: 571\n",
      "Epoch 1: 学习率 = 0.000125\n",
      "Epoch 2: 学习率 = 0.000125\n",
      "Epoch 3: 学习率 = 0.000125\n",
      "Epoch 4: 学习率 = 0.000125\n",
      "Epoch 5: 学习率 = 0.000125\n",
      "Epoch 6: 学习率 = 0.000125\n",
      "Epoch 7: 学习率 = 0.000125\n",
      "Epoch 8: 学习率 = 0.000125\n",
      "Epoch 9: 学习率 = 0.000125\n",
      "Epoch 10: 学习率 = 0.000125\n",
      "Epoch 11: 学习率 = 0.000125\n",
      "Epoch 12: 学习率 = 0.000125\n",
      "Epoch 13: 学习率 = 0.000125\n",
      "Epoch 14: 学习率 = 0.000125\n",
      "Epoch 15: 学习率 = 0.000125\n",
      "Epoch 16: 学习率 = 0.000125\n",
      "Epoch 17: 学习率 = 0.000125\n",
      "Epoch 18: 学习率 = 0.000125\n",
      "Epoch 19: 学习率 = 0.000125\n",
      "Epoch 20: 学习率 = 0.000125\n",
      "Epoch 21: 学习率 = 0.000125\n",
      "Epoch 22: 学习率 = 0.000125\n",
      "Epoch 23: 学习率 = 0.000125\n",
      "Epoch 24: 学习率 = 0.000125\n",
      "Epoch 25: 学习率 = 0.000125\n",
      "Epoch 26: 学习率 = 0.000125\n",
      "Epoch 27: 学习率 = 0.000125\n",
      "Epoch 28: 学习率 = 0.000125\n",
      "Epoch 29: 学习率 = 0.000125\n",
      "Epoch 30: 学习率 = 0.000125\n",
      "Epoch 31: 学习率 = 0.000125\n",
      "Epoch 32: 学习率 = 0.000125\n",
      "Epoch 33: 学习率 = 0.000125\n",
      "Epoch 34: 学习率 = 0.000125\n",
      "Epoch 35: 学习率 = 0.000125\n",
      "Epoch 36: 学习率 = 0.000125\n",
      "Epoch 37: 学习率 = 0.000125\n",
      "Epoch 38: 学习率 = 0.000125\n",
      "Epoch 39: 学习率 = 0.000125\n",
      "Epoch 40: 学习率 = 0.000125\n",
      "Epoch 41: 学习率 = 0.000125\n",
      "Epoch 42: 学习率 = 0.000125\n",
      "Epoch 43: 学习率 = 0.000125\n",
      "Epoch 44: 学习率 = 0.000125\n",
      "Epoch 45: 学习率 = 0.000125\n",
      "Epoch 46: 学习率 = 0.000125\n",
      "Epoch 47: 学习率 = 0.000125\n",
      "Epoch 48: 学习率 = 0.000125\n",
      "Epoch 49: 学习率 = 0.000125\n",
      "Epoch 50: 学习率 = 0.000125\n",
      "Epoch 51: 学习率 = 0.000125\n",
      "Epoch 52: 学习率 = 0.000125\n",
      "Epoch 53: 学习率 = 0.000125\n",
      "Epoch 54: 学习率 = 0.000125\n",
      "Epoch 55: 学习率 = 0.000125\n",
      "Epoch 56: 学习率 = 0.000125\n",
      "Epoch 57: 学习率 = 0.000125\n",
      "Epoch 58: 学习率 = 0.000125\n",
      "Epoch 59: 学习率 = 0.000125\n",
      "Epoch 60: 学习率 = 0.000125\n",
      "Epoch 61: 学习率 = 0.000125\n",
      "Epoch 62: 学习率 = 0.000125\n",
      "Epoch 63: 学习率 = 0.000125\n",
      "Epoch 64: 学习率 = 0.000125\n",
      "Epoch 65: 学习率 = 0.000125\n",
      "Epoch 66: 学习率 = 0.000125\n",
      "Epoch 67: 学习率 = 0.000125\n",
      "Epoch 68: 学习率 = 0.000125\n",
      "Epoch 69: 学习率 = 0.000125\n",
      "Epoch 70: 学习率 = 0.000125\n",
      "Epoch 71: 学习率 = 0.000125\n",
      "Epoch 72: 学习率 = 0.000125\n",
      "Epoch 73: 学习率 = 0.000125\n",
      "Epoch 74: 学习率 = 0.000125\n",
      "Epoch 75: 学习率 = 0.000125\n",
      "Epoch 76: 学习率 = 0.000125\n",
      "Epoch 77: 学习率 = 0.000125\n",
      "Epoch 78: 学习率 = 0.000125\n",
      "Epoch 79: 学习率 = 0.000125\n",
      "Epoch 80: 学习率 = 0.000125\n",
      "Epoch 81: 学习率 = 0.000125\n",
      "Epoch 82: 学习率 = 0.000125\n",
      "Epoch 83: 学习率 = 0.000125\n",
      "Epoch 84: 学习率 = 0.000125\n",
      "Epoch 85: 学习率 = 0.000125\n",
      "Epoch 86: 学习率 = 0.000125\n",
      "Epoch 87: 学习率 = 0.000125\n",
      "Epoch 88: 学习率 = 0.000125\n",
      "Epoch 89: 学习率 = 0.000125\n",
      "Epoch 90: 学习率 = 0.000125\n",
      "Epoch 91: 学习率 = 0.000125\n",
      "Epoch 92: 学习率 = 0.000125\n",
      "Epoch 93: 学习率 = 0.000125\n",
      "Epoch 94: 学习率 = 0.000125\n",
      "Epoch 95: 学习率 = 0.000125\n",
      "Epoch 96: 学习率 = 0.000125\n",
      "Epoch 97: 学习率 = 0.000125\n",
      "Epoch 98: 学习率 = 0.000125\n",
      "Epoch 99: 学习率 = 0.000125\n",
      "Epoch 100: 学习率 = 0.000125\n",
      "Epoch 101: 学习率 = 0.000125\n",
      "Epoch 102: 学习率 = 0.000125\n",
      "Epoch 103: 学习率 = 0.000125\n",
      "Epoch 104: 学习率 = 0.000125\n",
      "Epoch 105: 学习率 = 0.000125\n",
      "Epoch 106: 学习率 = 0.000125\n",
      "Epoch 107: 学习率 = 0.000125\n",
      "Epoch 108: 学习率 = 0.000125\n",
      "Epoch 109: 学习率 = 0.000125\n",
      "Epoch 110: 学习率 = 0.000125\n",
      "Epoch 111: 学习率 = 0.000125\n",
      "Epoch 112: 学习率 = 0.000125\n",
      "Epoch 113: 学习率 = 0.000125\n",
      "Epoch 114: 学习率 = 0.000125\n",
      "Epoch 115: 学习率 = 0.000125\n",
      "Epoch 116: 学习率 = 0.000125\n",
      "Epoch 117: 学习率 = 0.000125\n",
      "Epoch 118: 学习率 = 0.000125\n",
      "Epoch 119: 学习率 = 0.000125\n",
      "Epoch 120: 学习率 = 0.000125\n",
      "Epoch 121: 学习率 = 0.000125\n",
      "Epoch 122: 学习率 = 0.000125\n",
      "Epoch 123: 学习率 = 0.000125\n",
      "Epoch 124: 学习率 = 0.000125\n",
      "Epoch 125: 学习率 = 0.000125\n",
      "Epoch 126: 学习率 = 0.000125\n",
      "Epoch 127: 学习率 = 0.000125\n",
      "Epoch 128: 学习率 = 0.000125\n",
      "Epoch 129: 学习率 = 0.000125\n",
      "Epoch 130: 学习率 = 0.000125\n",
      "Epoch 131: 学习率 = 0.000125\n",
      "Epoch 132: 学习率 = 0.000125\n",
      "Epoch 133: 学习率 = 0.000125\n",
      "Epoch 134: 学习率 = 0.000125\n",
      "Epoch 135: 学习率 = 0.000125\n",
      "Epoch 136: 学习率 = 0.000125\n",
      "Epoch 137: 学习率 = 0.000125\n",
      "Epoch 138: 学习率 = 0.000125\n",
      "Epoch 139: 学习率 = 0.000125\n",
      "Epoch 140: 学习率 = 0.000125\n",
      "Epoch 141: 学习率 = 0.000125\n",
      "Epoch 142: 学习率 = 0.000125\n",
      "Epoch 143: 学习率 = 0.000125\n",
      "Epoch 144: 学习率 = 0.000125\n",
      "Epoch 145: 学习率 = 0.000125\n",
      "Epoch 146: 学习率 = 0.000125\n",
      "Epoch 147: 学习率 = 0.000125\n",
      "Epoch 148: 学习率 = 0.000125\n",
      "Epoch 149: 学习率 = 0.000125\n",
      "Epoch 150: 学习率 = 0.000125\n",
      "Epoch 151: 学习率 = 0.000125\n",
      "Epoch 152: 学习率 = 0.000125\n",
      "Epoch 153: 学习率 = 0.000125\n",
      "Epoch 154: 学习率 = 0.000125\n",
      "Epoch 155: 学习率 = 0.000125\n",
      "Epoch 156: 学习率 = 0.000125\n",
      "Epoch 157: 学习率 = 0.000125\n",
      "Epoch 158: 学习率 = 0.000125\n",
      "Epoch 159: 学习率 = 0.000125\n",
      "Epoch 160: 学习率 = 0.000125\n",
      "Epoch 161: 学习率 = 0.000125\n",
      "Epoch 162: 学习率 = 0.000125\n",
      "Epoch 163: 学习率 = 0.000125\n",
      "Epoch 164: 学习率 = 0.000125\n",
      "Epoch 165: 学习率 = 0.000125\n",
      "Epoch 166: 学习率 = 0.000125\n",
      "Epoch 167: 学习率 = 0.000125\n",
      "Epoch 168: 学习率 = 0.000125\n",
      "Epoch 169: 学习率 = 0.000125\n",
      "Epoch 170: 学习率 = 0.000125\n",
      "Epoch 171: 学习率 = 0.000125\n",
      "Epoch 172: 学习率 = 0.000125\n",
      "Epoch 173: 学习率 = 0.000125\n",
      "Epoch 174: 学习率 = 0.000125\n",
      "Epoch 175: 学习率 = 0.000125\n",
      "Epoch 176: 学习率 = 0.000125\n",
      "Epoch 177: 学习率 = 0.000125\n",
      "Epoch 178: 学习率 = 0.000125\n",
      "Epoch 179: 学习率 = 0.000125\n",
      "Epoch 180: 学习率 = 0.000125\n",
      "Epoch 181: 学习率 = 0.000125\n",
      "Epoch 182: 学习率 = 0.000125\n",
      "Epoch 183: 学习率 = 0.000125\n",
      "Epoch 184: 学习率 = 0.000125\n",
      "Epoch 185: 学习率 = 0.000125\n",
      "Epoch 186: 学习率 = 0.000125\n",
      "Epoch 187: 学习率 = 0.000125\n",
      "Epoch 188: 学习率 = 0.000125\n",
      "Epoch 189: 学习率 = 0.000125\n",
      "Epoch 190: 学习率 = 0.000125\n",
      "Epoch 191: 学习率 = 0.000125\n",
      "Epoch 192: 学习率 = 0.000125\n",
      "Epoch 193: 学习率 = 0.000125\n",
      "Epoch 194: 学习率 = 0.000125\n",
      "Epoch 195: 学习率 = 0.000125\n",
      "Epoch 196: 学习率 = 0.000125\n",
      "Epoch 197: 学习率 = 0.000125\n",
      "Epoch 198: 学习率 = 0.000125\n",
      "Epoch 199: 学习率 = 0.000125\n",
      "Epoch 200: 学习率 = 0.000125\n",
      "Epoch 201: 学习率 = 0.000125\n",
      "Epoch 202: 学习率 = 0.000125\n",
      "Epoch 203: 学习率 = 0.000125\n",
      "Epoch 204: 学习率 = 0.000125\n",
      "Epoch 205: 学习率 = 0.000125\n",
      "Epoch 206: 学习率 = 0.000125\n",
      "Epoch 207: 学习率 = 0.000125\n",
      "Epoch 208: 学习率 = 0.000125\n",
      "Epoch 209: 学习率 = 0.000125\n",
      "Epoch 210: 学习率 = 0.000125\n",
      "Epoch 211: 学习率 = 0.000125\n",
      "Epoch 212: 学习率 = 0.000125\n",
      "Epoch 213: 学习率 = 0.000125\n",
      "Epoch 214: 学习率 = 0.000125\n",
      "Epoch 215: 学习率 = 0.000125\n",
      "Epoch 216: 学习率 = 0.000125\n",
      "Epoch 217: 学习率 = 0.000125\n",
      "Epoch 218: 学习率 = 0.000125\n",
      "Epoch 219: 学习率 = 0.000125\n",
      "Epoch 220: 学习率 = 0.000125\n",
      "Epoch 221: 学习率 = 0.000125\n",
      "Epoch 222: 学习率 = 0.000125\n",
      "Epoch 223: 学习率 = 0.000125\n",
      "Epoch 224: 学习率 = 0.000125\n",
      "Epoch 225: 学习率 = 0.000125\n",
      "Epoch 226: 学习率 = 0.000125\n",
      "Epoch 227: 学习率 = 0.000125\n",
      "Epoch 228: 学习率 = 0.000125\n",
      "Epoch 229: 学习率 = 0.000125\n",
      "Epoch 230: 学习率 = 0.000125\n",
      "Epoch 231: 学习率 = 0.000125\n",
      "Epoch 232: 学习率 = 0.000125\n",
      "Epoch 233: 学习率 = 0.000125\n",
      "Epoch 234: 学习率 = 0.000125\n",
      "Epoch 235: 学习率 = 0.000125\n",
      "Epoch 236: 学习率 = 0.000125\n",
      "Epoch 237: 学习率 = 0.000125\n",
      "Epoch 238: 学习率 = 0.000125\n",
      "Epoch 239: 学习率 = 0.000125\n",
      "Epoch 240: 学习率 = 0.000125\n",
      "Epoch 241: 学习率 = 0.000125\n",
      "Epoch 242: 学习率 = 0.000125\n",
      "Epoch 243: 学习率 = 0.000125\n",
      "Epoch 244: 学习率 = 0.000125\n",
      "Epoch 245: 学习率 = 0.000125\n",
      "Epoch 246: 学习率 = 0.000125\n",
      "Epoch 247: 学习率 = 0.000125\n",
      "Epoch 248: 学习率 = 0.000125\n",
      "Epoch 249: 学习率 = 0.000125\n",
      "Epoch 250: 学习率 = 0.000125\n",
      "Epoch 251: 学习率 = 0.000125\n",
      "Epoch 252: 学习率 = 0.000125\n",
      "Epoch 253: 学习率 = 0.000125\n",
      "Epoch 254: 学习率 = 0.000125\n",
      "Epoch 255: 学习率 = 0.000125\n",
      "Epoch 256: 学习率 = 0.000125\n",
      "Epoch 257: 学习率 = 0.000125\n",
      "Epoch 258: 学习率 = 0.000125\n",
      "Epoch 259: 学习率 = 0.000125\n",
      "Epoch 260: 学习率 = 0.000125\n",
      "Epoch 261: 学习率 = 0.000125\n",
      "Epoch 262: 学习率 = 0.000125\n",
      "Epoch 263: 学习率 = 0.000125\n",
      "Epoch 264: 学习率 = 0.000125\n",
      "Epoch 265: 学习率 = 0.000125\n",
      "Epoch 266: 学习率 = 0.000125\n",
      "Epoch 267: 学习率 = 0.000125\n",
      "Epoch 268: 学习率 = 0.000125\n",
      "Epoch 269: 学习率 = 0.000125\n",
      "Epoch 270: 学习率 = 0.000125\n",
      "Epoch 271: 学习率 = 0.000125\n",
      "Epoch 272: 学习率 = 0.000125\n",
      "Epoch 273: 学习率 = 0.000125\n",
      "Epoch 274: 学习率 = 0.000125\n",
      "Epoch 275: 学习率 = 0.000125\n",
      "Epoch 276: 学习率 = 0.000125\n",
      "Epoch 277: 学习率 = 0.000125\n",
      "Epoch 278: 学习率 = 0.000125\n",
      "Epoch 279: 学习率 = 0.000125\n",
      "Epoch 280: 学习率 = 0.000125\n",
      "Epoch 281: 学习率 = 0.000125\n",
      "Epoch 282: 学习率 = 0.000125\n",
      "Epoch 283: 学习率 = 0.000125\n",
      "Epoch 284: 学习率 = 0.000125\n",
      "Epoch 285: 学习率 = 0.000125\n",
      "Epoch 286: 学习率 = 0.000125\n",
      "Epoch 287: 学习率 = 0.000125\n",
      "Epoch 288: 学习率 = 0.000125\n",
      "Epoch 289: 学习率 = 0.000125\n",
      "Epoch 290: 学习率 = 0.000125\n",
      "Epoch 291: 学习率 = 0.000125\n",
      "Epoch 292: 学习率 = 0.000125\n",
      "Epoch 293: 学习率 = 0.000125\n",
      "Epoch 294: 学习率 = 0.000125\n",
      "Epoch 295: 学习率 = 0.000125\n",
      "Epoch 296: 学习率 = 0.000125\n",
      "Epoch 297: 学习率 = 0.000125\n",
      "Epoch 298: 学习率 = 0.000125\n",
      "Epoch 299: 学习率 = 0.000125\n",
      "Epoch 300: 学习率 = 0.000125\n",
      "Epoch 301: 学习率 = 0.000125\n",
      "Epoch 302: 学习率 = 0.000125\n",
      "Epoch 303: 学习率 = 0.000125\n",
      "Epoch 304: 学习率 = 0.000125\n",
      "Epoch 305: 学习率 = 0.000125\n",
      "Epoch 306: 学习率 = 0.000125\n",
      "Epoch 307: 学习率 = 0.000125\n",
      "Epoch 308: 学习率 = 0.000125\n",
      "Epoch 309: 学习率 = 0.000125\n",
      "Epoch 310: 学习率 = 0.000125\n",
      "Epoch 311: 学习率 = 0.000125\n",
      "Epoch 312: 学习率 = 0.000125\n",
      "Epoch 313: 学习率 = 0.000125\n",
      "Epoch 314: 学习率 = 0.000125\n",
      "Epoch 315: 学习率 = 0.000125\n",
      "Epoch 316: 学习率 = 0.000125\n",
      "Epoch 317: 学习率 = 0.000125\n",
      "Epoch 318: 学习率 = 0.000125\n",
      "Epoch 319: 学习率 = 0.000125\n",
      "Epoch 320: 学习率 = 0.000125\n",
      "Epoch 321: 学习率 = 0.000125\n",
      "Epoch 322: 学习率 = 0.000125\n",
      "Epoch 323: 学习率 = 0.000125\n",
      "Epoch 324: 学习率 = 0.000125\n",
      "Epoch 325: 学习率 = 0.000125\n",
      "Epoch 326: 学习率 = 0.000125\n",
      "Epoch 327: 学习率 = 0.000125\n",
      "Epoch 328: 学习率 = 0.000125\n",
      "Epoch 329: 学习率 = 0.000125\n",
      "Epoch 330: 学习率 = 0.000125\n",
      "Epoch 331: 学习率 = 0.000125\n",
      "Epoch 332: 学习率 = 0.000125\n",
      "Epoch 333: 学习率 = 0.000125\n",
      "Epoch 334: 学习率 = 0.000125\n",
      "Epoch 335: 学习率 = 0.000125\n",
      "Epoch 336: 学习率 = 0.000125\n",
      "Epoch 337: 学习率 = 0.000125\n",
      "Epoch 338: 学习率 = 0.000125\n",
      "Epoch 339: 学习率 = 0.000125\n",
      "Epoch 340: 学习率 = 0.000125\n",
      "Epoch 341: 学习率 = 0.000125\n",
      "Epoch 342: 学习率 = 0.000125\n",
      "Epoch 343: 学习率 = 0.000125\n",
      "Epoch 344: 学习率 = 0.000125\n",
      "Epoch 345: 学习率 = 0.000125\n",
      "Epoch 346: 学习率 = 0.000125\n",
      "Epoch 347: 学习率 = 0.000125\n",
      "Epoch 348: 学习率 = 0.000125\n",
      "Epoch 349: 学习率 = 0.000125\n",
      "Epoch 350: 学习率 = 0.000125\n",
      "Epoch 351: 学习率 = 0.000125\n",
      "Epoch 352: 学习率 = 0.000125\n",
      "Epoch 353: 学习率 = 0.000125\n",
      "Epoch 354: 学习率 = 0.000125\n",
      "Epoch 355: 学习率 = 0.000125\n",
      "Epoch 356: 学习率 = 0.000125\n",
      "Epoch 357: 学习率 = 0.000125\n",
      "Epoch 358: 学习率 = 0.000125\n",
      "Epoch 359: 学习率 = 0.000125\n",
      "Epoch 360: 学习率 = 0.000125\n",
      "Epoch 361: 学习率 = 0.000125\n",
      "Epoch 362: 学习率 = 0.000125\n",
      "Epoch 363: 学习率 = 0.000125\n",
      "Epoch 364: 学习率 = 0.000125\n",
      "Epoch 365: 学习率 = 0.000125\n",
      "Epoch 366: 学习率 = 0.000125\n",
      "Epoch 367: 学习率 = 0.000125\n",
      "Epoch 368: 学习率 = 0.000125\n",
      "Epoch 369: 学习率 = 0.000125\n",
      "Epoch 370: 学习率 = 0.000125\n",
      "Epoch 371: 学习率 = 0.000125\n",
      "Epoch 372: 学习率 = 0.000125\n",
      "Epoch 373: 学习率 = 0.000125\n",
      "Epoch 374: 学习率 = 0.000125\n",
      "Epoch 375: 学习率 = 0.000125\n",
      "Epoch 376: 学习率 = 0.000125\n",
      "Epoch 377: 学习率 = 0.000125\n",
      "Epoch 378: 学习率 = 0.000125\n",
      "Epoch 379: 学习率 = 0.000125\n",
      "Epoch 380: 学习率 = 0.000125\n",
      "Epoch 381: 学习率 = 0.000125\n",
      "Epoch 382: 学习率 = 0.000125\n",
      "Epoch 383: 学习率 = 0.000125\n",
      "Epoch 384: 学习率 = 0.000125\n",
      "Epoch 385: 学习率 = 0.000125\n",
      "Epoch 386: 学习率 = 0.000125\n",
      "Epoch 387: 学习率 = 0.000125\n",
      "Epoch 388: 学习率 = 0.000125\n",
      "Epoch 389: 学习率 = 0.000125\n",
      "Epoch 390: 学习率 = 0.000125\n",
      "Epoch 391: 学习率 = 0.000125\n",
      "Epoch 392: 学习率 = 0.000125\n",
      "Epoch 393: 学习率 = 0.000125\n",
      "Epoch 394: 学习率 = 0.000125\n",
      "Epoch 395: 学习率 = 0.000125\n",
      "Epoch 396: 学习率 = 0.000125\n",
      "Epoch 397: 学习率 = 0.000125\n",
      "Epoch 398: 学习率 = 0.000125\n",
      "Epoch 399: 学习率 = 0.000125\n",
      "Epoch 400: 学习率 = 0.000125\n",
      "Epoch 401: 学习率 = 0.000125\n",
      "Epoch 402: 学习率 = 0.000125\n",
      "Epoch 403: 学习率 = 0.000125\n",
      "Epoch 404: 学习率 = 0.000125\n",
      "Epoch 405: 学习率 = 0.000125\n",
      "Epoch 406: 学习率 = 0.000125\n",
      "Epoch 407: 学习率 = 0.000125\n",
      "Epoch 408: 学习率 = 0.000125\n",
      "Epoch 409: 学习率 = 0.000125\n",
      "Epoch 410: 学习率 = 0.000125\n",
      "Epoch 411: 学习率 = 0.000125\n",
      "Epoch 412: 学习率 = 0.000125\n",
      "Epoch 413: 学习率 = 0.000125\n",
      "Epoch 414: 学习率 = 0.000125\n",
      "Epoch 415: 学习率 = 0.000125\n",
      "Epoch 416: 学习率 = 0.000125\n",
      "Epoch 417: 学习率 = 0.000125\n",
      "Epoch 418: 学习率 = 0.000125\n",
      "Epoch 419: 学习率 = 0.000125\n",
      "Epoch 420: 学习率 = 0.000125\n",
      "Epoch 421: 学习率 = 0.000125\n",
      "Epoch 422: 学习率 = 0.000125\n",
      "Epoch 423: 学习率 = 0.000125\n",
      "Epoch 424: 学习率 = 0.000125\n",
      "Epoch 425: 学习率 = 0.000125\n",
      "Epoch 426: 学习率 = 0.000125\n",
      "Epoch 427: 学习率 = 0.000125\n",
      "Epoch 428: 学习率 = 0.000125\n",
      "Epoch 429: 学习率 = 0.000125\n",
      "Epoch 430: 学习率 = 0.000125\n",
      "Epoch 431: 学习率 = 0.000125\n",
      "Epoch 432: 学习率 = 0.000125\n",
      "Epoch 433: 学习率 = 0.000125\n",
      "Epoch 434: 学习率 = 0.000125\n",
      "Epoch 435: 学习率 = 0.000125\n",
      "Epoch 436: 学习率 = 0.000125\n",
      "Epoch 437: 学习率 = 0.000125\n",
      "Epoch 438: 学习率 = 0.000125\n",
      "Epoch 439: 学习率 = 0.000125\n",
      "Epoch 440: 学习率 = 0.000125\n",
      "Epoch 441: 学习率 = 0.000125\n",
      "Epoch 442: 学习率 = 0.000125\n",
      "Epoch 443: 学习率 = 0.000125\n",
      "Epoch 444: 学习率 = 0.000125\n",
      "Epoch 445: 学习率 = 0.000125\n",
      "Epoch 446: 学习率 = 0.000125\n",
      "Epoch 447: 学习率 = 0.000125\n",
      "Epoch 448: 学习率 = 0.000125\n",
      "Epoch 449: 学习率 = 0.000125\n",
      "Epoch 450: 学习率 = 0.000125\n",
      "Epoch 451: 学习率 = 0.000125\n",
      "Epoch 452: 学习率 = 0.000125\n",
      "Epoch 453: 学习率 = 0.000125\n",
      "Epoch 454: 学习率 = 0.000125\n",
      "Epoch 455: 学习率 = 0.000125\n",
      "Epoch 456: 学习率 = 0.000125\n",
      "Epoch 457: 学习率 = 0.000125\n",
      "Epoch 458: 学习率 = 0.000125\n",
      "Epoch 459: 学习率 = 0.000125\n",
      "Epoch 460: 学习率 = 0.000125\n",
      "Epoch 461: 学习率 = 0.000125\n",
      "Epoch 462: 学习率 = 0.000125\n",
      "Epoch 463: 学习率 = 0.000125\n",
      "Epoch 464: 学习率 = 0.000125\n",
      "Epoch 465: 学习率 = 0.000125\n",
      "Epoch 466: 学习率 = 0.000125\n",
      "Epoch 467: 学习率 = 0.000125\n",
      "Epoch 468: 学习率 = 0.000125\n",
      "Epoch 469: 学习率 = 0.000125\n",
      "Epoch 470: 学习率 = 0.000125\n",
      "Epoch 471: 学习率 = 0.000125\n",
      "Epoch 472: 学习率 = 0.000125\n",
      "Epoch 473: 学习率 = 0.000125\n",
      "Epoch 474: 学习率 = 0.000125\n",
      "Epoch 475: 学习率 = 0.000125\n",
      "Epoch 476: 学习率 = 0.000125\n",
      "Epoch 477: 学习率 = 0.000125\n",
      "Epoch 478: 学习率 = 0.000125\n",
      "Epoch 479: 学习率 = 0.000125\n",
      "Epoch 480: 学习率 = 0.000125\n",
      "Epoch 481: 学习率 = 0.000125\n",
      "Epoch 482: 学习率 = 0.000125\n",
      "Epoch 483: 学习率 = 0.000125\n",
      "Epoch 484: 学习率 = 0.000125\n",
      "Epoch 485: 学习率 = 0.000125\n",
      "Epoch 486: 学习率 = 0.000125\n",
      "Epoch 487: 学习率 = 0.000125\n",
      "Epoch 488: 学习率 = 0.000125\n",
      "Epoch 489: 学习率 = 0.000125\n",
      "Epoch 490: 学习率 = 0.000125\n",
      "Epoch 491: 学习率 = 0.000125\n",
      "Epoch 492: 学习率 = 0.000125\n",
      "Epoch 493: 学习率 = 0.000125\n",
      "Epoch 494: 学习率 = 0.000125\n",
      "Epoch 495: 学习率 = 0.000125\n",
      "Epoch 496: 学习率 = 0.000125\n",
      "Epoch 497: 学习率 = 0.000125\n",
      "Epoch 498: 学习率 = 0.000125\n",
      "Epoch 499: 学习率 = 0.000125\n",
      "Epoch 500: 学习率 = 0.000125\n",
      "Epoch 501: 学习率 = 0.000125\n",
      "Epoch 502: 学习率 = 0.000125\n",
      "Epoch 503: 学习率 = 0.000125\n",
      "Epoch 504: 学习率 = 0.000125\n",
      "Epoch 505: 学习率 = 0.000125\n",
      "Epoch 506: 学习率 = 0.000125\n",
      "Epoch 507: 学习率 = 0.000125\n",
      "Epoch 508: 学习率 = 0.000125\n",
      "Epoch 509: 学习率 = 0.000125\n",
      "Epoch 510: 学习率 = 0.000125\n",
      "Epoch 511: 学习率 = 0.000125\n",
      "Epoch 512: 学习率 = 0.000125\n",
      "Epoch 513: 学习率 = 0.000125\n",
      "Epoch 514: 学习率 = 0.000125\n",
      "Epoch 515: 学习率 = 0.000125\n",
      "Epoch 516: 学习率 = 0.000125\n",
      "Epoch 517: 学习率 = 0.000125\n",
      "Epoch 518: 学习率 = 0.000125\n",
      "Epoch 519: 学习率 = 0.000125\n",
      "Epoch 520: 学习率 = 0.000125\n",
      "Epoch 521: 学习率 = 0.000125\n",
      "Epoch 522: 学习率 = 0.000125\n",
      "Epoch 523: 学习率 = 0.000125\n",
      "Epoch 524: 学习率 = 0.000125\n",
      "Epoch 525: 学习率 = 0.000125\n",
      "Epoch 526: 学习率 = 0.000125\n",
      "Epoch 527: 学习率 = 0.000125\n",
      "Epoch 528: 学习率 = 0.000125\n",
      "Epoch 529: 学习率 = 0.000125\n",
      "Epoch 530: 学习率 = 0.000125\n",
      "Epoch 531: 学习率 = 0.000125\n",
      "Epoch 532: 学习率 = 0.000125\n",
      "Epoch 533: 学习率 = 0.000125\n",
      "Epoch 534: 学习率 = 0.000125\n",
      "Epoch 535: 学习率 = 0.000125\n",
      "Epoch 536: 学习率 = 0.000125\n",
      "Epoch 537: 学习率 = 0.000125\n",
      "Epoch 538: 学习率 = 0.000125\n",
      "Epoch 539: 学习率 = 0.000125\n",
      "Epoch 540: 学习率 = 0.000125\n",
      "Epoch 541: 学习率 = 0.000125\n",
      "Epoch 542: 学习率 = 0.000125\n",
      "Epoch 543: 学习率 = 0.000125\n",
      "Epoch 544: 学习率 = 0.000125\n",
      "Epoch 545: 学习率 = 0.000125\n",
      "Epoch 546: 学习率 = 0.000125\n",
      "Epoch 547: 学习率 = 0.000125\n",
      "Epoch 548: 学习率 = 0.000125\n",
      "Epoch 549: 学习率 = 0.000125\n",
      "Epoch 550: 学习率 = 0.000125\n",
      "Epoch 551: 学习率 = 0.000125\n",
      "Epoch 552: 学习率 = 0.000125\n",
      "Epoch 553: 学习率 = 0.000125\n",
      "Epoch 554: 学习率 = 0.000125\n",
      "Epoch 555: 学习率 = 0.000125\n",
      "Epoch 556: 学习率 = 0.000125\n",
      "Epoch 557: 学习率 = 0.000125\n",
      "Epoch 558: 学习率 = 0.000125\n",
      "Epoch 559: 学习率 = 0.000125\n",
      "Epoch 560: 学习率 = 0.000125\n",
      "Epoch 561: 学习率 = 0.000125\n",
      "Epoch 562: 学习率 = 0.000125\n",
      "Epoch 563: 学习率 = 0.000125\n",
      "Epoch 564: 学习率 = 0.000125\n",
      "Epoch 565: 学习率 = 0.000125\n",
      "Epoch 566: 学习率 = 0.000125\n",
      "Epoch 567: 学习率 = 0.000125\n",
      "Epoch 568: 学习率 = 0.000125\n",
      "Epoch 569: 学习率 = 0.000125\n",
      "Epoch 570: 学习率 = 0.000125\n",
      "Epoch 571: 学习率 = 0.000125\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 125ms/step\n",
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 2ms/step \n",
      "RMSEc (校正均方根误差): 5.659834786903921\n",
      "RMSEp (预测均方根误差): 9.361121346768044\n",
      "Rcal (校正集相关系数): 0.9963255717506613\n",
      "Rval (验证集相关系数): 0.9921809546760988\n",
      "RPD (相对预测偏差): 7.877799885448076\n",
      "Training time: 84.92925143241882 seconds\n",
      "Testing time: 0.5362663269042969 seconds\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHHCAYAAABeLEexAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+2UlEQVR4nO3deXhTVeL/8XeSNumatnQvlH1HQAXFqoALQ0FkRBlHHVRE1NEBFdz5qoiMI67jOqKOCzqKKP5cWcSCgAoVkH0XpFC2lqW0aUvX5P7+uG0gskPbBPp5PU+eJPee3HvubaCfnnPuuRbDMAxERERE6jGrvysgIiIi4m8KRCIiIlLvKRCJiIhIvadAJCIiIvWeApGIiIjUewpEIiIiUu8pEImIiEi9p0AkIiIi9Z4CkYiIiNR7CkQiAe6WW26hadOmJ/XZMWPGYLFYarZCAWbz5s1YLBYmTJhQ5/u2WCyMGTPG+37ChAlYLBY2b958zM82bdqUW265pUbrcyrfFZH6ToFI5CRZLJbjesyZM8ffVa337rnnHiwWCxs3bjximUcffRSLxcKKFSvqsGYnbseOHYwZM4Zly5b5uype1aH0hRde8HdVRE5akL8rIHK6+t///ufz/sMPPyQjI+OQ5e3atTul/fz3v//F4/Gc1Gcfe+wxHnnkkVPa/5lg0KBBvPbaa0ycOJHRo0cftswnn3xCx44d6dSp00nv56abbuL666/H4XCc9DaOZceOHTz55JM0bdqUs88+22fdqXxXROo7BSKRk3TjjTf6vP/ll1/IyMg4ZPkf7d+/n7CwsOPeT3Bw8EnVDyAoKIigIP0z79atGy1btuSTTz45bCDKzMwkKyuLZ5555pT2Y7PZsNlsp7SNU3Eq3xWR+k5dZiK16JJLLuGss85i8eLF9OjRg7CwMP7v//4PgK+//pp+/fqRkpKCw+GgRYsW/POf/8Ttdvts44/jQg7unnj77bdp0aIFDoeD8847j0WLFvl89nBjiCwWC8OHD+err77irLPOwuFw0KFDB7777rtD6j9nzhy6du1KSEgILVq04K233jrucUk//fQT1157LY0bN8bhcJCamsrIkSMpKSk55PgiIiLYvn07AwYMICIigvj4eB544IFDzkV+fj633HILUVFRREdHM3jwYPLz849ZFzBbidatW8eSJUsOWTdx4kQsFgs33HAD5eXljB49mi5duhAVFUV4eDjdu3dn9uzZx9zH4cYQGYbBU089RaNGjQgLC+PSSy9l9erVh3w2Ly+PBx54gI4dOxIREYHT6aRv374sX77cW2bOnDmcd955AAwZMsTbLVs9fupwY4iKi4u5//77SU1NxeFw0KZNG1544QUMw/ApdyLfi5O1a9cuhg4dSmJiIiEhIXTu3JkPPvjgkHKTJk2iS5cuREZG4nQ66dixI6+88op3fUVFBU8++SStWrUiJCSE2NhYLr74YjIyMmqsrlL/6E9HkVq2d+9e+vbty/XXX8+NN95IYmIiYP7yjIiI4L777iMiIoIffviB0aNH43K5eP7554+53YkTJ1JYWMjf//53LBYLzz33HNdccw2bNm06ZkvBzz//zBdffME//vEPIiMjefXVVxk4cCDZ2dnExsYCsHTpUvr06UNycjJPPvkkbrebsWPHEh8ff1zHPXnyZPbv389dd91FbGwsCxcu5LXXXmPbtm1MnjzZp6zb7SY9PZ1u3brxwgsvMHPmTF588UVatGjBXXfdBZjB4qqrruLnn3/mzjvvpF27dnz55ZcMHjz4uOozaNAgnnzySSZOnMi5557rs+/PPvuM7t2707hxY/bs2cM777zDDTfcwO23305hYSHvvvsu6enpLFy48JBuqmMZPXo0Tz31FFdccQVXXHEFS5YsoXfv3pSXl/uU27RpE1999RXXXnstzZo1Izc3l7feeouePXuyZs0aUlJSaNeuHWPHjmX06NHccccddO/eHYALL7zwsPs2DIM///nPzJ49m6FDh3L22WczY8YMHnzwQbZv385LL73kU/54vhcnq6SkhEsuuYSNGzcyfPhwmjVrxuTJk7nlllvIz8/n3nvvBSAjI4MbbriByy+/nGeffRaAtWvXMm/ePG+ZMWPGMG7cOG677TbOP/98XC4Xv/76K0uWLOFPf/rTKdVT6jFDRGrEsGHDjD/+k+rZs6cBGG+++eYh5ffv33/Isr///e9GWFiYUVpa6l02ePBgo0mTJt73WVlZBmDExsYaeXl53uVff/21ARjffvutd9kTTzxxSJ0Aw263Gxs3bvQuW758uQEYr732mndZ//79jbCwMGP79u3eZRs2bDCCgoIO2ebhHO74xo0bZ1gsFmPLli0+xwcYY8eO9Sl7zjnnGF26dPG+/+qrrwzAeO6557zLKisrje7duxuA8f777x+zTuedd57RqFEjw+12e5d99913BmC89dZb3m2WlZX5fG7fvn1GYmKiceutt/osB4wnnnjC+/799983ACMrK8swDMPYtWuXYbfbjX79+hkej8db7v/+7/8MwBg8eLB3WWlpqU+9DMP8WTscDp9zs2jRoiMe7x+/K9Xn7KmnnvIp95e//MWwWCw+34Hj/V4cTvV38vnnnz9imZdfftkAjI8++si7rLy83EhLSzMiIiIMl8tlGIZh3HvvvYbT6TQqKyuPuK3OnTsb/fr1O2qdRE6UusxEapnD4WDIkCGHLA8NDfW+LiwsZM+ePXTv3p39+/ezbt26Y273uuuuIyYmxvu+urVg06ZNx/xsr169aNGihfd9p06dcDqd3s+63W5mzpzJgAEDSElJ8ZZr2bIlffv2Peb2wff4iouL2bNnDxdeeCGGYbB06dJDyt95550+77t37+5zLNOmTSMoKMjbYgTmmJ277777uOoD5rivbdu28eOPP3qXTZw4EbvdzrXXXuvdpt1uB8Dj8ZCXl0dlZSVdu3Y9bHfb0cycOZPy8nLuvvtun27GESNGHFLW4XBgtZr/Jbvdbvbu3UtERARt2rQ54f1WmzZtGjabjXvuucdn+f33349hGEyfPt1n+bG+F6di2rRpJCUlccMNN3iXBQcHc88991BUVMTcuXMBiI6Opri4+KjdX9HR0axevZoNGzaccr1EqikQidSyhg0ben/BHmz16tVcffXVREVF4XQ6iY+P9w7ILigoOOZ2Gzdu7PO+Ohzt27fvhD9b/fnqz+7atYuSkhJatmx5SLnDLTuc7OxsbrnlFho0aOAdF9SzZ0/g0OMLCQk5pCvu4PoAbNmyheTkZCIiInzKtWnT5rjqA3D99ddjs9mYOHEiAKWlpXz55Zf07dvXJ1x+8MEHdOrUyTs+JT4+nqlTpx7Xz+VgW7ZsAaBVq1Y+y+Pj4332B2b4eumll2jVqhUOh4O4uDji4+NZsWLFCe/34P2npKQQGRnps7z6ysfq+lU71vfiVGzZsoVWrVp5Q9+R6vKPf/yD1q1b07dvXxo1asStt956yDimsWPHkp+fT+vWrenYsSMPPvhgwE+XIIFPgUiklh3cUlItPz+fnj17snz5csaOHcu3335LRkaGd8zE8Vw6faSrmYw/DJat6c8eD7fbzZ/+9CemTp3Kww8/zFdffUVGRoZ38O8fj6+ursxKSEjgT3/6E//v//0/Kioq+PbbbyksLGTQoEHeMh999BG33HILLVq04N133+W7774jIyODyy67rFYvaX/66ae577776NGjBx999BEzZswgIyODDh061Nml9LX9vTgeCQkJLFu2jG+++cY7/qlv374+Y8V69OjB77//znvvvcdZZ53FO++8w7nnnss777xTZ/WUM48GVYv4wZw5c9i7dy9ffPEFPXr08C7PysryY60OSEhIICQk5LATGR5tcsNqK1eu5LfffuODDz7g5ptv9i4/lauAmjRpwqxZsygqKvJpJVq/fv0JbWfQoEF89913TJ8+nYkTJ+J0Ounfv793/eeff07z5s354osvfLq5nnjiiZOqM8CGDRto3ry5d/nu3bsPaXX5/PPPufTSS3n33Xd9lufn5xMXF+d9fyIzjzdp0oSZM2dSWFjo00pU3SVbXb+60KRJE1asWIHH4/FpJTpcXex2O/3796d///54PB7+8Y9/8NZbb/H44497WygbNGjAkCFDGDJkCEVFRfTo0YMxY8Zw22231dkxyZlFLUQiflD9l/jBf3mXl5fzxhtv+KtKPmw2G7169eKrr75ix44d3uUbN248ZNzJkT4PvsdnGIbPpdMn6oorrqCyspLx48d7l7ndbl577bUT2s6AAQMICwvjjTfeYPr06VxzzTWEhIQcte4LFiwgMzPzhOvcq1cvgoODee2113y29/LLLx9S1mazHdISM3nyZLZv3+6zLDw8HOC4phu44oorcLvdvP766z7LX3rpJSwWy3GPB6sJV1xxBTk5OXz66afeZZWVlbz22mtERER4u1P37t3r8zmr1eqdLLOsrOywZSIiImjZsqV3vcjJUAuRiB9ceOGFxMTEMHjwYO9tJf73v//VadfEsYwZM4bvv/+eiy66iLvuusv7i/Wss8465m0j2rZtS4sWLXjggQfYvn07TqeT//f//t8pjUXp378/F110EY888gibN2+mffv2fPHFFyc8viYiIoIBAwZ4xxEd3F0GcOWVV/LFF19w9dVX069fP7KysnjzzTdp3749RUVFJ7Sv6vmUxo0bx5VXXskVV1zB0qVLmT59uk+rT/V+x44dy5AhQ7jwwgtZuXIlH3/8sU/LEkCLFi2Ijo7mzTffJDIykvDwcLp160azZs0O2X///v259NJL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4s0332TlypXceuutREdHn1B/bk1zBNkYcHZDJszfzP9bsp1L2iT4rS4iInWlpMJN+9Ez6ny/a8amE2avmV97Y8eO5U9/+pP3fYMGDejcubP3/T//+U++/PJLvvnmG4YPH37E7dxyyy3ccMMNADz99NO8+uqrLFy4kD59+hy2fEVFBW+++SYtWrQAYPjw4YwdO9a7/rXXXmPUqFFcffXVALz++uve1pqTsWHDBr755hvmzZvHhRdeCMDHH39MamoqX331Fddeey3Z2dkMHDiQjh07AtC8eXPv57OzsznnnHPo2rUrYLaSBSq/thDNnz+fq666in79+tG0aVP+8pe/0Lt3bxYuXAiYf0W8/PLLPPbYY1x11VV06tSJDz/8kB07dngT79q1a/nuu+9455136NatGxdffDGvvfYakyZNYseOHYD5wysvL+e9996jQ4cOXH/99dxzzz38+9//9tehe13SxuxT/i2n0M81ERGR41X9C75aUVERDzzwAO3atSM6OpqIiAjWrl17zBaiTp06eV+Hh4fjdDrZtWvXEcuHhYV5wxBAcnKyt3xBQQG5ubmcf/753vU2m40uXbqc0LEdbO3atQQFBdGtWzfvstjYWNq0acPatWsBuOeee3jqqae46KKLeOKJJ1ixYoW37F133cWkSZM4++yzeeihh5g/f/5J16W2+bWF6MILL+Ttt9/mt99+o3Xr1ixfvpyff/7ZG1SysrLIycmhV69e3s9ERUXRrVs3MjMzuf7668nMzCQ6Otrny9mrVy+sVisLFizg6quvJjMzkx49evjc+C09PZ1nn32Wffv2ERMTU3cH/QdxEQ4A8vaX+60OIiJ1KTTYxpqx6X7Zb00JDw/3ef/AAw+QkZHBCy+8QMuWLQkNDeUvf/kL5eVH/7/9j7easFgseDyeEypfk12BJ+O2224jPT2dqVOn8v333zNu3DhefPFF7r77bvr27cuWLVuYNm0aGRkZXH755QwbNowXXnjBr3U+HL+2ED3yyCNcf/31tG3bluDgYM455xxGjBjBoEGDAPOGgACJiYk+n0tMTPSuy8nJISHBt6spKCiIBg0a+JQ53DYO3sfBysrKcLlcPo/aEhthhrR9xeV+/1KLiNQFi8VCmD2ozh+1OVv2vHnzuOWWW7j66qvp2LEjSUlJbN68udb2dzhRUVEkJiayaNEi7zK3282SJUtOepvt2rWjsrKSBQsWeJft3buX9evX0759e++y1NRU7rzzTr744gvuv/9+/vvf/3rXxcfHM3jwYD766CNefvll3n777ZOuT23yawvRZ599xscff8zEiRPp0KEDy5YtY8SIEaSkpDB48GC/1WvcuHE8+eSTdbKvmDAzEFV6DFyllUSFnvyN6URExD9atWrFF198Qf/+/bFYLDz++ONHbempLXfffTfjxo2jZcuWtG3bltdee419+/YdVxhcuXIlkZGR3vcWi4XOnTtz1VVXcfvtt/PWW28RGRnJI488QsOGDbnqqqsAGDFiBH379qV169bs27eP2bNn065dOwBGjx5Nly5d6NChA2VlZUyZMsW7LtD4NRA9+OCD3lYigI4dO7JlyxbGjRvH4MGDvfMv5Obmkpyc7P1cbm4uZ599NmBeHvnH/tbKykry8vK8n09KSiI3N9enTPX7w83xMGrUKO677z7ve5fLRWpq6ike7eGFBNsIt9soLneTV1yuQCQichr697//za233sqFF15IXFwcDz/8cK32LhzJww8/TE5ODjfffDM2m4077riD9PR0bLZjdxf26NHD573NZqOyspL333+fe++9lyuvvJLy8nJ69OjBtGnTvN13brebYcOGsW3bNpxOJ3369OGll14CzLmURo0axebNmwkNDaV79+5MmjSp5g+8Jhh+1KBBA+ONN97wWfb0008brVq1MgzDMDwej5GUlGS88MIL3vUFBQWGw+EwPvnkE8MwDGPNmjUGYPz666/eMjNmzDAsFouxfft2wzAM44033jBiYmKM8vJyb5lRo0YZbdq0Oa56FhQUGIBRUFBwcgd6DBc9M8to8vAU49fNebWyfRERfykpKTHWrFljlJSU+Lsq9ZLb7TZat25tPPbYY/6uSq052nfsRH5/+3UMUf/+/fnXv/7F1KlT2bx5M19++SX//ve/vZcLWiwWRowYwVNPPcU333zDypUrufnmm0lJSfFOMtWuXTv69OnD7bffzsKFC5k3bx7Dhw/n+uuvJyUlBYC//e1v2O12hg4dyurVq/n000955ZVXfFqB/Ck23Ow2yyvWwGoRETl5W7Zs4b///S+//fYbK1eu5K677iIrK4u//e1v/q5awPNrl9lrr73G448/zj/+8Q927dpFSkoKf//73xk9erS3zEMPPURxcTF33HEH+fn5XHzxxXz33XfeOYjAvKx++PDhXH755VitVgYOHMirr77qXR8VFcX333/PsGHD6NKlC3FxcYwePdqvcxAdLCb8wMBqERGRk2W1WpkwYQIPPPAAhmFw1llnMXPmzIAdtxNILIahS5uOxeVyERUVRUFBAU6ns8a3f99ny/hiyXYe7tOWuy5pcewPiIicJkpLS8nKyqJZs2Y+f8iK1JSjfcdO5Pe37mUWABqEVXeZlfm5JiIiIvWTAlEAiAgxey73l7v9XBMREZH6SYEoANiDzB9DWWXdz1khIiIiCkQBwRFkzg9RrkAkIiLiFwpEAcDhbSFSl5mIiIg/KBAFAIe6zERERPxKgSgAVI8hUpeZiMiZ45JLLmHEiBHe902bNuXll18+6mcsFgtfffXVKe+7prZTnygQBYDqMURqIRIR8b/+/fvTp0+fw6776aefsFgsrFix4oS3u2jRohqfEHjMmDHee3sebOfOnfTt27dG9/VHEyZMIDo6ulb3UZcUiAKAI1hjiEREAsXQoUPJyMhg27Zth6x7//336dq1K506dTrh7cbHxxMWFlYTVTympKQkHA5HnezrTKFAFAC8Y4gq1EIkIuJvV155JfHx8UyYMMFneVFREZMnT2bo0KHs3buXG264gYYNGxIWFkbHjh355JNPjrrdP3aZbdiwgR49ehASEkL79u3JyMg45DMPP/wwrVu3JiwsjObNm/P4449TUVEBmC00Tz75JMuXL8disWCxWLx1/mOX2cqVK7nssssIDQ0lNjaWO+64g6KiIu/6W265hQEDBvDCCy+QnJxMbGwsw4YN8+7rZGRnZ3PVVVcRERGB0+nkr3/9K7m5ud71y5cv59JLLyUyMhKn00mXLl349ddfAfOebP379ycmJobw8HA6dOjAtGnTTroux8Ov9zITU3UgKncrEIlIPWAYULG/7vcbHAYWyzGLBQUFcfPNNzNhwgQeffRRLFWfmTx5Mm63mxtuuIGioiK6dOnCww8/jNPpZOrUqdx00020aNGC888//5j78Hg8XHPNNSQmJrJgwQIKCgp8xhtVi4yMZMKECaSkpLBy5Upuv/12IiMjeeihh7juuutYtWoV3333HTNnzgTMe3f+UXFxMenp6aSlpbFo0SJ27drFbbfdxvDhw31C3+zZs0lOTmb27Nls3LiR6667jrPPPpvbb7/9mMdzuOOrDkNz586lsrKSYcOGcd111zFnzhwABg0axDnnnMP48eOx2WwsW7aM4OBgAIYNG0Z5eTk//vgj4eHhrFmzhoiIiBOux4lQIAoA3jFEaiESkfqgYj88nVL3+/2/HWAPP66it956K88//zxz587lkksuAczusoEDBxIVFUVUVBQPPPCAt/zdd9/NjBkz+Oyzz44rEM2cOZN169YxY8YMUlLMc/H0008fMu7nscce875u2rQpDzzwAJMmTeKhhx4iNDSUiIgIgoKCSEpKOuK+Jk6cSGlpKR9++CHh4ebxv/766/Tv359nn32WxMREAGJiYnj99dex2Wy0bduWfv36MWvWrJMKRLNmzWLlypVkZWWRmpoKwIcffkiHDh1YtGgR5513HtnZ2Tz44IO0bdsWgFatWnk/n52dzcCBA+nYsSMAzZs3P+E6nCh1mQUAzUMkIhJY2rZty4UXXsh7770HwMaNG/npp58YOnQoAG63m3/+85907NiRBg0aEBERwYwZM8jOzj6u7a9du5bU1FRvGAJIS0s7pNynn37KRRddRFJSEhERETz22GPHvY+D99W5c2dvGAK46KKL8Hg8rF+/3rusQ4cO2Gw27/vk5GR27dp1Qvs6eJ+pqaneMATQvn17oqOjWbt2LQD33Xcft912G7169eKZZ57h999/95a95557eOqpp7jooot44oknTmoQ+4lSC1EA0GX3IlKvBIeZrTX+2O8JGDp0KHfffTf/+c9/eP/992nRogU9e/YE4Pnnn+eVV17h5ZdfpmPHjoSHhzNixAjKy8trrLqZmZkMGjSIJ598kvT0dKKiopg0aRIvvvhije3jYNXdVdUsFgseT+39XhozZgx/+9vfmDp1KtOnT+eJJ55g0qRJXH311dx2222kp6czdepUvv/+e8aNG8eLL77I3XffXWv1UQtRANBl9yJSr1gsZtdVXT+OY/zQwf76179itVqZOHEiH374Ibfeeqt3PNG8efO46qqruPHGG+ncuTPNmzfnt99+O+5tt2vXjq1bt7Jz507vsl9++cWnzPz582nSpAmPPvooXbt2pVWrVmzZssWnjN1ux+0+eu9Cu3btWL58OcXFxd5l8+bNw2q10qZNm+Ou84moPr6tW7d6l61Zs4b8/Hzat2/vXda6dWtGjhzJ999/zzXXXMP777/vXZeamsqdd97JF198wf33389///vfWqlr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b775hq5du3LttdeSkJDAOeecw3//+1/v+qysLHJycujVq5d3WVRUFN26dSMzMxOAzMxMoqOjvWEIoFevXlitVhYsWOAt06NHD+x2u7dMeno669evZ9++fbV9mMcWFGI+GdVjiBSIRERE6pJfA9GmTZsYP348rVq1YsaMGdx1113cc889fPDBBwDk5OQAkJiY6PO5xMRE77qcnBwSEhJ81gcFBdGgQQOfMofbxsH7OFhZWRkul8vnUauquswOBCJ1mYmIiNQlv3aZeTweunbtytNPPw3AOeecw6pVq3jzzTcZPHiw3+o1btw4nnzyybrbYVWXWZCnagyRusxERETqlF9biJKTk2nfvr3Psnbt2pGdnQ1AUlISALm5uT5lcnNzveuSkpLYtWuXz/rKykry8vJ8yhxuGwfv42CjRo2ioKDA+9i6devJHuLxqbrs3lYViNRCJCIiUrf8Goguuugi1q9f77Pst99+o0mTJoA5wDopKYlZs2Z517tcLhYsWEBaWhoAaWlp5Ofns3jxYm+ZH374AY/HQ7du3bxlfvzxRyoqKrxlMjIyaNOmjc8VbdUcDgdOp9PnUasOCURqIRIREalLfg1EI0eO5JdffuHpp59m48aNTJw4kbfffpthw4YBYLFYGDFiBE899RTffPMNK1eu5OabbyYlJYUBAwYAZotSnz59uP3221m4cCHz5s1j+PDhXH/99aSkpADwt7/9DbvdztChQ1m9ejWffvopr7zyCvfdd5+/Dt2XzRxDZK0OROoyExERqVN+HUN03nnn8eWXXzJq1CjGjh1Ls2bNePnllxk0aJC3zEMPPURxcTF33HEH+fn5XHzxxXz33XeEhIR4y3z88ccMHz6cyy+/HKvVysCBA3n11Ve966Oiovj+++8ZNmwYXbp0IS4ujtGjRwfGJffgbSGqDkSah0hERKRuWQzDMPxdiUDncrmIioqioKCgdrrPivfA8y0AaFb6ESnR4cx75LKa34+IiEg9ciK/v/1+6w7B22UG5u07NIZIRESkbikQBYKqLjMw73hfVqEuMxERkbqkQBQI/tBCVFxeiXoyRURE6o4CUSCwWA66430FHkOTM4qIiNQlBaJAUdVt5rCacyUVllUcrbSIiIjUIAWiQFHVbRYdbHaVFZdpHJGIiEhdUSAKFFV3vHcGm0GouKzSn7URERGpVxSIAkXVHe+d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bbcxY8aMkzn02ucdQ1QIHnPMUPVs1dv3KRCJiIjUCuMk7dy501iyZInhdru9yxYsWGCsXbv2hLZTWFhotGrVysjIyDB69uxp3HvvvYZhGEZ+fr4RHBxsTJ482Vt27dq1BmBkZmYahmEY06ZNM6xWq5GTk+MtM378eMPpdBplZWWGYRjGQw89ZHTo0MFnn9ddd52Rnp5+3HUsKCgwAKOgoOCEju2klO83jCec5qPE3N+/v19vNHl4ivHw58trf/8iIiJniBP5/X1SLUQASUlJnHPOOezYscN75/vzzz+ftm3bntB2hg0bRr9+/ejVq5fP8sWLF1NRUeGzvG3btjRu3JjMzEwAMjMz6dixI4mJid4y6enpuFwuVq9e7S3zx22np6d7t3E4ZWVluFwun0edCQoBm918XdVtltrAnNtp6779dVcPERGReuSkApHH42Hs2LFERUXRpEkTmjRpQnR0NP/85z/xeI7/0vBJkyaxZMkSxo0bd8i6nJwc7HY70dHRPssTExPJycnxljk4DFWvr153tDIul4uSksN3QY0bN46oqCjvIzU19biP6ZRZLIeMI0qNMbvMtuapy0xERKQ2nNRM1Y8++ijvvvsuzzzzDBdddBEAP//8M2PGjKG0tJR//etfx9zG1q1buffee8nIyCAkJORkqlFrRo0axX333ed973K56jYUhURB8W7vXETVLUQ78ktwewxsVkvd1UVERKQeOKlA9MEHH/DOO+9473IP0KlTJxo2bMg//vGP4wpEixcvZteuXZx77rneZW63mx9//JHXX3+dGTNmUF5eTn5+vk8rUW5uLklJSYDZbbdw4UKf7VZfhXZwmT9emZabm4vT6SQ0NPSwdXM4HDgcjmMeQ635QwtRojOEYJuFCrfBzoISGsXo9igiIiI16aS6zPLy8g47Vqht27bk5eUd1zYuv/xyVq5cybJly7yPrl27MmjQIO/r4OBgZs2a5f3M+vXryc7O9l7VlpaWxsqVK9m1a5e3TEZGBk6nk/bt23vLHLyN6jLV2whIDt8bvNqsFhpGq9tMRESktpxUIOrcuTOvv/76Ictff/31Qy6dP5LIyEjOOussn0d4eDixsbGcddZZREVFMXToUO677z5mz57N4sWLGTJkCGlpaVxwwQUA9O7dm/bt23PTTTexfPlyZsyYwWOPPcawYcO8LTx33nknmzZt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BQV8/vnnDB48mLlz5/q7WgFv69at3HvvvWRkZBASEuLv6pxW+vbt633dqVMnunXrRpMmTfjss88IDQ31Y83qJ3WZ+UFcXBw2m+2QqwVyc3NJSkryU60CX/W5Odp5S0pKYteuXT7rKysrycvLqzfndvjw4UyZMoXZs2fTqFEj7/KkpCTKy8vJz8/3Kf/H83e481u97kxmt9tp2bIlXbp0Ydy4cXTu3JlXXnlF5+0YFi9ezK5duzj33HMJCgoiKCiIuXPn8uqrrxIUFERiYqLO33GKjo6mdevWbNy4Ud87P1Ag8gO73U6XLl2YNWuWd5nH42HWrFmkpaX5sWaBrVmzZiQlJfmcN5fLxYIFC7znLS0tjfz8fBYvXuwt88MPP+DxeOjWrVud17kuGYbB8OHD+fLLL/nhhx9o1qyZz/ouXboQHBzsc/7Wr19Pdna2z/lbuXKlT6jMyMjA6XTSvn37ujmQAOHxeCgrK9N5O4bLL7+clStXsmzZMu+ja9euDBo0yPta5+/4FBUV8fvvv5OcnKzvnT/4e1R3fTVp0iTD4XAYEyZMMNasWWPccccdRnR0tM/VAvVRYWGhsXTpUmPp0qUGYPz73/82li5damzZssUwDPOy++joaOPrr782VqxYYVx11VWHvez+nHPOMRYsWGD8/PPPRqtWrerFZfd33XWXERUVZcyZM8fnMt79+/d7y9x5551G48aNjR9++MH49ddfjbS0NCMtLc27vvoy3t69exvLli0zvvvuOyM+Pv6Mv4z3kUceMebOnWtkZWUZK1asMB555BHDYrEY33//vWEYOm8n6uCrzAxD5+9I7r//fmPOnDlGVlaWMW/ePKNXr15GXFycsWvXLsMwdN7qmgKRH7322mtG48aNDbvdbpx//vnGL7/84u8q+d3s2bMN4JDH4MGDDcMwL71//PHHjcTERMPhcBiXX365sX79ep9t7N2717jhhhuMiIgIw+l0GkOGDDEKCwv9cDR163DnDTDef/99b5mSkhLjH//4hxETE2OEhYUZV199tbFz506f7WzevNno27evERoaasTFxRn333+/UVFRUcdHU7duvfVWo0mTJobdbjfi4+ONyy+/3BuGDEPn7UT9MRDp/B3eddddZyQnJxt2u91o2LChcd111xkbN270rtd5q1sWwzAM/7RNiYiIiAQGjSESERGRek+BSEREROo9BSIRERGp9xSIREREpN5TIBIREZF6T4FIRERE6j0FIhEREan3FIhERI6TxWLhq6++8nc1RKQWKBCJyGnhlltuwWKxHPLo06ePv6smImeAIH9XQETkePXp04f333/fZ5nD4fBTbUTkTKIWIhE5bTgcDpKSknweMTExgNmdNX78ePr27UtoaCjNmzfn888/9/n8ypUrueyyywgNDSU2NpY77riDoqIinzLvvfceHTp0wOFwkJyczPDhw33W79mzh6uvvpqwsDBatWrFN9984123b98+Bg0aRHx8PKGhobRq1eqQACcigUmBSETOGI8//jgDBw5k+fLlDBo0iOuvv561a9cCUFxcTHp6OjExMSxatIjJkyczc+ZMn8Azfvx4hg0bxh133MHKlSv55ptvaNmypc8+nnzySf7617+yYsUKrrjiCgYNGkReXp53/2vWrGH69OmsXbuW8ePHExcXV3cnQEROnr/vLisicjwGDx5s2Gw2Izw83Ofxr3/9yzAMwwCMO++80+cz3bp1M+666y7DMAzj7bffNmJiYoyioiLv+qlTpxpWq9XIyckxDMMwUlJSjEcfffSIdQCMxx57zPu+qKjIAIzp06cbhmEY/fv3N4YMGVIzBywidUpjiETktHHppZcyfvx4n2UNGjTwvk5LS/NZl5aWxrJlywBYu3YtnTt3Jjw83Lv+oosuwuPxsH79eiwWCzt27ODyyy8/ah06derkfR0eHo7T6WTXrl0A3HXXXQwcOJAlS5bQu3dvBgwYwIUXXnhSxyoidUuBSEROG+Hh4Yd0YdWU0NDQ4yoXHBzs895iseDxeADo27cvW7ZsYdq0aWRkZHD55ZczbNgwXnjhhRqvr4jULI0hEpEzxi+//HLI+3bt2gHQrl07li9fTnFxsXf9vHnzsFqttGnThsjISJo2bcqsWbNOqQ7x8fEMHjyYjz76iJdffpm33377lLYnInVDLUQictooKysjJyfHZ1lQUJB34PLkyZPp2rUrF198MR9//DELFy7k3XffBWDQoEE88cQTDB48mDFjxrB7927uvvtubrrpJhITEwEYM2YMd955JwkJCfTt25fCwkLmzZvH3XfffVz1Gz16NF26dKFDhw6UlZUxZcoUbyATkcCmQCQip43vvvuO5ORkn2Vt2rRh3bp1gHkF2KRJk/jHP/5BcnIyn3zyCe3btwcgLCyMGTNmcO+993LeeecRFhbGwIED+fe//+3d1uDBgyktLeWll17igQceIC4ujr/85S/HXT+73c6oUaPYvHkzoaGhdO/enUmTJtXAkYtIbbMYhmH4uxIiIqfKYrHw5ZdfMmDAAH9XRUROQxpDJCIiIvWeApGIiIjUexpDJCJnBPX+i8ipUAuRiIiI1HsKRCIiIlLvKRCJiIhIvadAJCIiIvWeApGIiIjUewpEIiIiUu8pEImIiEi9p0AkIiIi9Z4CkYiIiNR7/x9hqHQ2ApjSfwAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmMAAAHHCAYAAADzrV8YAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAA9hAAAPYQGoP6dpAABN5UlEQVR4nO3de1xVZd7///cGZIPoBhUByROZ5onMwWAY83YMDItxxLyzHCokyw6alt1155Sa91SUhzIbJ3NqsplJFMumMs1h0DQVURFLRc3KyiwwJU4WkOzr90c/1rcdqGxDl4fX8/HYD93X9dlrf9YlPfa7tdZeOIwxRgAAALCFj90NAAAAXMgIYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAGzXuXNnjR492u42cIF477335HA49Nprr9ndCiCJMAacNxYuXCiHw6GtW7fa3co5xeFweDxcLpcGDhyod95555S3uWjRIs2ZM6fpmjzH1IWd4z0WL15sd4vAWcXP7gYAYO/evfLxse//DQcPHqxbbrlFxhh9/vnnev755zV06FCtXLlSSUlJXm9v0aJF2rlzp+69996mb/YcMmHCBF1xxRX1xuPj423oBjh7EcYANKljx47J7XbL39+/0a9xOp2nsaOT69atm2666Sbr+YgRI9SzZ089++yzpxTGLgRHjx5VUFDQCWsGDBig//7v/z5DHQHnLk5TAheYgwcP6tZbb1V4eLicTqd69eqlv/3tbx41NTU1mjp1qmJiYhQcHKygoCANGDBAa9as8aj77LPP5HA4NGvWLM2ZM0ddunSR0+lUYWGhHn30UTkcDn388ccaPXq0QkJCFBwcrPT0dH333Xce2/n5NWN1p1w3bNigSZMmqW3btgoKCtLw4cP1zTffeLzW7Xbr0UcfVWRkpJo3b65BgwapsLDwF12H1qNHD4WGhuqTTz7xGH/zzTeVnJysyMhIOZ1OdenSRX/6059UW1tr1fz2t7/VO++8o88//9w6Lde5c2drvrq6WtOmTdMll1wip9OpDh066MEHH1R1dXWjelu6dKliYmIUGBio0NBQ3XTTTTp48KA1P2vWLDkcDn3++ef1Xjt58mT5+/vr22+/tcby8vI0ZMgQBQcHq3nz5ho4cKA2bNjg8bq6f8vCwkL94Q9/UKtWrXTllVc2qt+TcTgcGj9+vF599VVdeumlCggIUExMjNatW1evtqCgQNdcc41cLpdatGihhIQEbdq0qV5daWmp7rvvPnXu3FlOp1Pt27fXLbfcosOHD3vUud1uPf7442rfvr0CAgKUkJCgjz/+2KNm3759GjFihCIiIhQQEKD27dvrxhtvVFlZWZPsPyBxZAy4oBQXF+vXv/619QHYtm1brVy5UmPGjFF5ebl1Wq28vFwvvviiRo0apdtvv10VFRV66aWXlJSUpM2bN+vyyy/32O7LL7+sqqoqjR07Vk6nU61bt7bmRo4cqaioKGVkZGjbtm168cUXFRYWpqeeeuqk/d5zzz1q1aqVpk2bps8++0xz5szR+PHjtWTJEqtm8uTJmjFjhoYOHaqkpCR98MEHSkpKUlVV1SmvU1lZmb799lt16dLFY3zhwoVq0aKFJk2apBYtWmj16tWaOnWqysvLNXPmTEnSww8/rLKyMn355Zd65plnJEktWrSQ9OOH/+9//3utX79eY8eOVY8ePbRjxw4988wz+uijj/Svf/3rhH0tXLhQ6enpuuKKK5SRkaHi4mI9++yz2rBhgwoKChQSEqKRI0fqwQcfVFZWlh544AGP12dlZenqq69Wq1atJEmrV6/WNddco5iYGE2bNk0+Pj56+eWXddVVV+n9999XbGysx+uvv/56de3aVU888YSMMSddx4qKinoBSJLatGkjh8NhPV+7dq2WLFmiCRMmyOl06i9/+YuGDBmizZs3q3fv3pKkXbt2acCAAXK5XHrwwQfVrFkzvfDCC/rtb3+rtWvXKi4uTpJUWVmpAQMGaPfu3br11lv1q1/9SocPH9Zbb72lL7/8UqGhodb7Pvnkk/Lx8dH//M//qKysTDNmzFBqaqry8vIk/fg/JUlJSaqurtY999yjiIgIHTx4UMuXL1dpaamCg4NPugZAoxgA54WXX37ZSDJbtmw5bs2YMWNMu3btzOHDhz3Gb7zxRhMcHGy+++47Y4wxx44dM9XV1R413377rQkPDze33nqrNbZ//34jybhcLnPo0CGP+mnTphlJHvXGGDN8+HDTpk0bj7FOnTqZtLS0evuSmJho3G63NX7fffcZX19fU1paaowxpqioyPj5+ZmUlBSP7T366KNGksc2j0eSGTNmjPnmm2/MoUOHzNatW82QIUOMJDNz5kyP2rr1+ak77rjDNG/e3FRVVVljycnJplOnTvVq//GPfxgfHx/z/vvve4zPnz/fSDIbNmw4bp81NTUmLCzM9O7d23z//ffW+PLly40kM3XqVGssPj7exMTEeLx+8+bNRpL5+9//bowxxu12m65du5qkpCSPNf7uu+9MVFSUGTx4sDVW9285atSo4/b3U2vWrDGSjvv4+uuvrdq6sa1bt1pjn3/+uQkICDDDhw+3xlJSUoy/v7/55JNPrLGvvvrKtGzZ0vzXf/2XNTZ16lQjySxbtqxeX3X7Wddfjx49PH7On332WSPJ7NixwxhjTEFBgZFkli5d2qj9Bk4VpymBC4QxRq+//rqGDh0qY4wOHz5sPZKSklRWVqZt27ZJknx9fa1rvtxut0pKSnTs2DH169fPqvmpESNGqG3btg2+75133unxfMCAATpy5IjKy8tP2vPYsWM9jqAMGDBAtbW11im4nJwcHTt2THfffbfH6+65556TbvunXnrpJbVt21ZhYWHq16+fcnJy9OCDD2rSpEkedYGBgdbf6476DBgwQN9995327Nlz0vdZunSpevTooe7du3us/1VXXSVJ9U4D/9TWrVt16NAh3X333QoICLDGk5OT1b17d49vf95www3Kz8/3OM26ZMkSOZ1ODRs2TJK0fft27du3T3/4wx905MgRq5ejR48qISFB69atk9vt9ujh5/+WJzN16lRlZ2fXe/z0yKn04wX9MTEx1vOOHTtq2LBhWrVqlWpra1VbW6t///vfSklJ0cUXX2zVtWvXTn/4wx+0fv166+fp9ddfV58+fTR8+PB6/fz0Z0mS0tPTPa5tHDBggCTp008/lSTryNeqVavqnVoHmhKnKYELxDfffKPS0lItWLBACxYsaLDm0KFD1t9feeUVzZ49W3v27NEPP/xgjUdFRdV7XUNjdTp27OjxvO4U2bfffiuXy3XCnk/0WklWKLvkkks86lq3bm3VNsawYcM0fvx41dTUaMuWLXriiSf03Xff1fuG565du/TII49o9erV9cJkY64h2rdvn3bv3n3c4PrT9f+5un299NJL6811795d69evt55ff/31mjRpkpYsWaI//vGPMsZo6dKl1vVWdb1IUlpa2nHfs6yszGMdT/Tv3JDo6GglJiaetK5r1671xrp166bvvvvOukbwu+++a3Dfe/ToIbfbrQMHDqhXr1765JNPNGLEiEb1d7Kfr6ioKE2aNElPP/20Xn31VQ0YMEC///3vddNNN3GKEk2KMAZcIOqOctx0003H/QC+7LLLJEn//Oc/NXr0aKWkpOiBBx5QWFiYfH19lZGRUe+idsnziNHP+fr6NjhuGnHN0S95rTfat29vhYZrr71WoaGhGj9+vAYNGqTrrrtO0o8XhQ8cOFAul0v/93//py5duiggIEDbtm3T//7v/9Y7itQQt9ut6OhoPf300w3Od+jQoUn2JzIyUgMGDFBWVpb++Mc/atOmTfriiy88rtOr63fmzJn1rgGsU3etW50T/Tufixrz8zV79myNHj1ab775pv79739rwoQJysjI0KZNm9S+ffsz1SrOc4Qx4ALRtm1btWzZUrW1tSc9WvHaa6/p4osv1rJlyzxO7UybNu10t+mVTp06SZI+/vhjj6M2R44c8fjGoLfuuOMOPfPMM3rkkUc0fPhwORwOvffeezpy5IiWLVum//qv/7Jq9+/fX+/1Pz8dVqdLly764IMPlJCQcNya46nb171791qnNevs3bvXmq9zww036O6779bevXu1ZMkSNW/eXEOHDvXoRZJcLlejjl6dTnVH6X7qo48+UvPmza2jiM2bN9fevXvr1e3Zs0c+Pj5WkO3SpYt27tzZpP1FR0crOjpajzzyiDZu3Kj+/ftr/vz5euyxx5r0fXDh4pox4ALh6+urESNG6PXXX2/ww+qnt4yoO2Lw0yMEeXl5ys3NPf2NeiEhIUF+fn56/vnnPcb//Oc//6Lt+vn56f7779fu3bv15ptvSmp4TWpqavSXv/yl3uuDgoIaPG05cuRIHTx4UH/961/rzX3//fc6evTocXvq16+fwsLCNH/+fI/bYKxcuVK7d+9WcnKyR/2IESPk6+urzMxMLV26VL/73e887gsWExOjLl26aNasWaqsrKz3fj+/hcjplJub63Et4oEDB/Tmm2/q6quvlq+vr3x9fXX11VfrzTff1GeffWbVFRcXa9GiRbryyiut068jRozQBx98oDfeeKPe+3h7RLW8vFzHjh3zGIuOjpaPj0+jb0UCNAZHxoDzzN/+9je9++679cYnTpyoJ598UmvWrFFcXJxuv/129ezZUyUlJdq2bZv+85//qKSkRJL0u9/9TsuWLdPw4cOVnJys/fv3a/78+erZs2eDH9x2CQ8P18SJEzV79mz9/ve/15AhQ/TBBx9o5cqVCg0N9fro00+NHj1aU6dO1VNPPaWUlBT95je/UatWrZSWlqYJEybI4XDoH//4R4Mf8DExMVqyZIkmTZqkK664Qi1atNDQoUN18803KysrS3feeafWrFmj/v37q7a2Vnv27FFWVpZWrVqlfv36NdhPs2bN9NRTTyk9PV0DBw7UqFGjrFtbdO7cWffdd59HfVhYmAYNGqSnn35aFRUVuuGGGzzmfXx89OKLL+qaa65Rr169lJ6erosuukgHDx7UmjVr5HK59Pbbb5/y+knS+++/3+AtRi677DLrlLgk9e7dW0lJSR63tpCk6dOnWzWPPfaYsrOzdeWVV+ruu++Wn5+fXnjhBVVXV2vGjBlW3QMPPKDXXntN119/vW699VbFxMSopKREb731lubPn68+ffo0uv/Vq1dr/Pjxuv7669WtWzcdO3ZM//jHP6z/sQGajF1f4wTQtOpuB3G8x4EDB4wxxhQXF5tx48aZDh06mGbNmpmIiAiTkJBgFixYYG3L7XabJ554wnTq1Mk4nU7Tt29fs3z5cpOWluZxy4a6W1v8/BYQxvy/2yF88803Dfa5f/9+a+x4t7b4+W066m5JsGbNGmvs2LFjZsqUKSYiIsIEBgaaq666yuzevdu0adPG3HnnnSddN0lm3LhxDc7V3SKj7v02bNhgfv3rX5vAwEATGRlpHnzwQbNq1ap6PVVWVpo//OEPJiQkxEjyWLOamhrz1FNPmV69ehmn02latWplYmJizPTp001ZWdlJ+12yZInp27evcTqdpnXr1iY1NdV8+eWXDdb+9a9/NZJMy5YtPW6H8VMFBQXmuuuuM23atDFOp9N06tTJjBw50uTk5Fg1x/u3PJ6T3dpi2rRpVm3d+v/zn/80Xbt2tX7efrqedbZt22aSkpJMixYtTPPmzc2gQYPMxo0b69UdOXLEjB8/3lx00UXG39/ftG/f3qSlpVm3dKnr7+e3rKj7eX755ZeNMcZ8+umn5tZbbzVdunQxAQEBpnXr1mbQoEHmP//5T6PWAWgshzFNfCUsANistLRUrVq10mOPPaaHH37Y7nZwAg6HQ+PGjfvFp5aBcxnXjAE4p33//ff1xubMmSPpx19NBABnO64ZA3BOW7JkiRYuXKhrr71WLVq00Pr165WZmamrr75a/fv3t7s9ADgpwhiAc9pll10mPz8/zZgxQ+Xl5dZF/dx2AMC5gmvGAAAAbMQ1YwAAADYijAEAANiIa8bOcm63W1999ZVatmz5i25gCQAAzhxjjCoqKhQZGSkfnxMf+yKMneW++uqrJvvlwQAA4Mw6cODASX+pPGHsLNeyZUtJP/5j1v3uNQAAcHYrLy9Xhw4drM/xEyGMneXqTk26XC7CGAAA55jGXGLEBfwAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYKOzIozNmzdPnTt3VkBAgOLi4rR58+YT1i9dulTdu3dXQECAoqOjtWLFCo95Y4ymTp2qdu3aKTAwUImJidq3b59HTUlJiVJTU+VyuRQSEqIxY8aosrLSmq+qqtLo0aMVHR0tPz8/paSk1Otj/fr16t+/v9q0aaPAwEB1795dzzzzzC/ePwAAcOGwPYwtWbJEkyZN0rRp07Rt2zb16dNHSUlJOnToUIP1Gzdu1KhRozRmzBgVFBQoJSVFKSkp2rlzp1UzY8YMzZ07V/Pnz1deXp6CgoKUlJSkqqoqqyY1NVW7du1Sdna2li9frnXr1mns2LHWfG1trQIDAzVhwgQlJiY22EtQUJDGjx+vdevWaffu3XrkkUf0yCOPaMGCBae8fwAA4AJjbBYbG2vGjRtnPa+trTWRkZEmIyOjwfqRI0ea5ORkj7G4uDhzxx13GGOMcbvdJiIiwsycOdOaLy0tNU6n02RmZhpjjCksLDSSzJYtW6yalStXGofDYQ4ePFjvPdPS0sywYcMatT/Dhw83N9100ynv38+VlZUZSaasrKxR9QAAwH7efH7bemSspqZG+fn5HkeefHx8lJiYqNzc3AZfk5ubW+9IVVJSklW/f/9+FRUVedQEBwcrLi7OqsnNzVVISIj69etn1SQmJsrHx0d5eXmnvD8FBQXauHGjBg4ceMr7BwAALix+dr754cOHVVtbq/DwcI/x8PBw7dmzp8HXFBUVNVhfVFRkzdeNnagmLCzMY97Pz0+tW7e2arzRvn17ffPNNzp27JgeffRR3Xbbbae8f9XV1aqurrael5eXe90PAAA4d9h+zdj54P3339fWrVs1f/58zZkzR5mZmae8rYyMDAUHB1uPDh06NGGnAADgbGNrGAsNDZWvr6+Ki4s9xouLixUREdHgayIiIk5YX/fnyWp+fgH9sWPHVFJSctz3PZGoqChFR0fr9ttv13333adHH330lPdv8uTJKisrsx4HDhzwuh8AAHDusDWM+fv7KyYmRjk5OdaY2+1WTk6O4uPjG3xNfHy8R70kZWdnW/VRUVGKiIjwqCkvL1deXp5VEx8fr9LSUuXn51s1q1evltvtVlxc3C/aJ7fbbZ1mPJX9czqdcrlcHg8AAHD+svWaMUmaNGmS0tLS1K9fP8XGxmrOnDk6evSo0tPTJUm33HKLLrroImVkZEiSJk6cqIEDB2r27NlKTk7W4sWLtXXrVut2Eg6HQ/fee68ee+wxde3aVVFRUZoyZYoiIyOte4X16NFDQ4YM0e2336758+frhx9+0Pjx43XjjTcqMjLS6q2wsFA1NTUqKSlRRUWFtm/fLkm6/PLLJf14/7COHTuqe/fukqR169Zp1qxZmjBhQqP3DwAAXODOwLc7T+q5554zHTt2NP7+/iY2NtZs2rTJmhs4cKBJS0vzqM/KyjLdunUz/v7+plevXuadd97xmHe73WbKlCkmPDzcOJ1Ok5CQYPbu3etRc+TIETNq1CjTokUL43K5THp6uqmoqPCo6dSpk5FU71Fn7ty5plevXqZ58+bG5XKZvn37mr/85S+mtra20ft3MtzaAgCAc483n98OY4yxMQviJMrLyxUcHKyysjJOWQIAcI7w5vObb1MCAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI1sD2Pz5s1T586dFRAQoLi4OG3evPmE9UuXLlX37t0VEBCg6OhorVixwmPeGKOpU6eqXbt2CgwMVGJiovbt2+dRU1JSotTUVLlcLoWEhGjMmDGqrKy05quqqjR69GhFR0fLz89PKSkp9fpYtmyZBg8erLZt28rlcik+Pl6rVq3yqKmtrdWUKVMUFRWlwMBAdenSRX/6059kjPFylQAAwPnK1jC2ZMkSTZo0SdOmTdO2bdvUp08fJSUl6dChQw3Wb9y4UaNGjdKYMWNUUFCglJQUpaSkaOfOnVbNjBkzNHfuXM2fP195eXkKCgpSUlKSqqqqrJrU1FTt2rVL2dnZWr58udatW6exY8da87W1tQoMDNSECROUmJjYYC/r1q3T4MGDtWLFCuXn52vQoEEaOnSoCgoKrJqnnnpKzz//vP785z9r9+7deuqppzRjxgw999xzv3TpAADA+cLYKDY21owbN856XltbayIjI01GRkaD9SNHjjTJyckeY3FxceaOO+4wxhjjdrtNRESEmTlzpjVfWlpqnE6nyczMNMYYU1hYaCSZLVu2WDUrV640DofDHDx4sN57pqWlmWHDhjVqf3r27GmmT59uPU9OTja33nqrR811111nUlNTG7U9Y4wpKyszkkxZWVmjXwMAAOzlzee3bUfGampqlJ+f73HkycfHR4mJicrNzW3wNbm5ufWOVCUlJVn1+/fvV1FRkUdNcHCw4uLirJrc3FyFhISoX79+Vk1iYqJ8fHyUl5d3yvvjdrtVUVGh1q1bW2O/+c1vlJOTo48++kiS9MEHH2j9+vW65pprjrud6upqlZeXezwAAMD5y8+uNz58+LBqa2sVHh7uMR4eHq49e/Y0+JqioqIG64uKiqz5urET1YSFhXnM+/n5qXXr1lbNqZg1a5YqKys1cuRIa+yhhx5SeXm5unfvLl9fX9XW1urxxx9XamrqcbeTkZGh6dOnn3IfAADg3GL7Bfzng0WLFmn69OnKysryCHpZWVl69dVXtWjRIm3btk2vvPKKZs2apVdeeeW425o8ebLKysqsx4EDB87ELgAAAJvYdmQsNDRUvr6+Ki4u9hgvLi5WREREg6+JiIg4YX3dn8XFxWrXrp1HzeWXX27V/PwLAseOHVNJSclx3/dEFi9erNtuu01Lly6tdwr1gQce0EMPPaQbb7xRkhQdHa3PP/9cGRkZSktLa3B7TqdTTqfT6z4AAMC5ybYjY/7+/oqJiVFOTo415na7lZOTo/j4+AZfEx8f71EvSdnZ2VZ9VFSUIiIiPGrKy8uVl5dn1cTHx6u0tFT5+flWzerVq+V2uxUXF+fVPmRmZio9PV2ZmZlKTk6uN//dd9/Jx8dziX19feV2u716HwAAcP6y7ciYJE2aNElpaWnq16+fYmNjNWfOHB09elTp6emSpFtuuUUXXXSRMjIyJEkTJ07UwIEDNXv2bCUnJ2vx4sXaunWrFixYIElyOBy699579dhjj6lr166KiorSlClTFBkZad0rrEePHhoyZIhuv/12zZ8/Xz/88IPGjx+vG2+8UZGRkVZvhYWFqqmpUUlJiSoqKrR9+3ZJso6wLVq0SGlpaXr22WcVFxdnXW8WGBio4OBgSdLQoUP1+OOPq2PHjurVq5cKCgr09NNP69Zbbz3dSwsAAM4VZ+DbnSf03HPPmY4dOxp/f38TGxtrNm3aZM0NHDjQpKWledRnZWWZbt26GX9/f9OrVy/zzjvveMy73W4zZcoUEx4ebpxOp0lISDB79+71qDly5IgZNWqUadGihXG5XCY9Pd1UVFR41HTq1MlIqvf4aW8Nzf+03/LycjNx4kTTsWNHExAQYC6++GLz8MMPm+rq6kavD7e2AADg3OPN57fDGG4HfzYrLy9XcHCwysrK5HK57G4HAAA0gjef33ybEgAAwEaEMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGz0i8JYVVVVU/UBAABwQfI6jLndbv3pT3/SRRddpBYtWujTTz+VJE2ZMkUvvfRSkzcIAABwPvM6jD322GNauHChZsyYIX9/f2u8d+/eevHFF5u0OQAAgPOd12Hs73//uxYsWKDU1FT5+vpa43369NGePXuatDkAAIDznddh7ODBg7rkkkvqjbvdbv3www9N0hQAAMCFwusw1rNnT73//vv1xl977TX17du3SZoCAAC4UPh5+4KpU6cqLS1NBw8elNvt1rJly7R37179/e9/1/Lly09HjwAAAOctr4+MDRs2TG+//bb+85//KCgoSFOnTtXu3bv19ttva/DgwaejRwAAgPOWwxhj7G4Cx1deXq7g4GCVlZXJ5XLZ3Q4AAGgEbz6/vT4ydvHFF+vIkSP1xktLS3XxxRd7uzkAAIALmtdh7LPPPlNtbW298erqah08eLBJmgIAALhQNPoC/rfeesv6+6pVqxQcHGw9r62tVU5Ojjp37tykzQEAAJzvGh3GUlJSJEkOh0NpaWkec82aNVPnzp01e/bsJm0OAADgfNfoMOZ2uyVJUVFR2rJli0JDQ09bUwAAABcKr+8ztn///tPRBwAAwAXJ6zAmSUePHtXatWv1xRdfqKamxmNuwoQJTdIYAADAhcDrb1MWFBTokksu0ahRozR+/Hg99thjuvfee/XHP/5Rc+bM8bqBefPmqXPnzgoICFBcXJw2b958wvqlS5eqe/fuCggIUHR0tFasWOExb4zR1KlT1a5dOwUGBioxMVH79u3zqCkpKVFqaqpcLpdCQkI0ZswYVVZWWvNVVVUaPXq0oqOj5efnZ10v91PLli3T4MGD1bZtW7lcLsXHx2vVqlX16g4ePKibbrpJbdq0UWBgoKKjo7V161YvVggAAJzPvA5j9913n4YOHapvv/1WgYGB2rRpkz7//HPFxMRo1qxZXm1ryZIlmjRpkqZNm6Zt27apT58+SkpK0qFDhxqs37hxo0aNGqUxY8aooKBAKSkpSklJ0c6dO62aGTNmaO7cuZo/f77y8vIUFBSkpKQkVVVVWTWpqanatWuXsrOztXz5cq1bt05jx4615mtraxUYGKgJEyYoMTGxwV7WrVunwYMHa8WKFcrPz9egQYM0dOhQFRQUWDXffvut+vfvr2bNmmnlypUqLCzU7Nmz1apVK6/WCQAAnMeMl4KDg82ePXusvxcWFhpjjNm0aZO59NJLvdpWbGysGTdunPW8trbWREZGmoyMjAbrR44caZKTkz3G4uLizB133GGMMcbtdpuIiAgzc+ZMa760tNQ4nU6TmZlpjDGmsLDQSDJbtmyxalauXGkcDoc5ePBgvfdMS0szw4YNa9T+9OzZ00yfPt16/r//+7/myiuvbNRrj6esrMxIMmVlZb9oOwAA4Mzx5vPb6yNjzZo1k4/Pjy8LCwvTF198IUkKDg7WgQMHGr2dmpoa5efnexx58vHxUWJionJzcxt8TW5ubr0jVUlJSVb9/v37VVRU5FETHBysuLg4qyY3N1chISHq16+fVZOYmCgfHx/l5eU1uv+fc7vdqqioUOvWra2xt956S/369dP111+vsLAw9e3bV3/9619PuJ3q6mqVl5d7PAAAwPnL6zDWt29fbdmyRZI0cOBATZ06Va+++qruvfde9e7du9HbOXz4sGpraxUeHu4xHh4erqKiogZfU1RUdML6uj9PVhMWFuYx7+fnp9atWx/3fRtj1qxZqqys1MiRI62xTz/9VM8//7y6du2qVatW6a677tKECRP0yiuvHHc7GRkZCg4Oth4dOnQ45Z4AAMDZz+sw9sQTT6hdu3aSpMcff1ytWrXSXXfdpW+++UYvvPBCkzd4Lli0aJGmT5+urKwsj6Dndrv1q1/9Sk888YT69u2rsWPH6vbbb9f8+fOPu63JkyerrKzMenhztBEAAJx7vL61xU9P74WFhendd989pTcODQ2Vr6+viouLPcaLi4sVERHR4GsiIiJOWF/3Z3FxsRUY655ffvnlVs3PvyBw7NgxlZSUHPd9T2Tx4sW67bbbtHTp0nqnUNu1a6eePXt6jPXo0UOvv/76cbfndDrldDq97gMAAJybvD4ydjzbtm3T7373u0bX+/v7KyYmRjk5OdaY2+1WTk6O4uPjG3xNfHy8R70kZWdnW/VRUVGKiIjwqCkvL1deXp5VEx8fr9LSUuXn51s1q1evltvtVlxcXKP7l6TMzEylp6crMzNTycnJ9eb79++vvXv3eox99NFH6tSpk1fvAwAAzmPefDPg3XffNffff7+ZPHmy+eSTT4wxxuzevdsMGzbM+Pj4mGuuucarbxosXrzYOJ1Os3DhQlNYWGjGjh1rQkJCTFFRkTHGmJtvvtk89NBDVv2GDRuMn5+fmTVrltm9e7eZNm2aadasmdmxY4dV8+STT5qQkBDz5ptvmg8//NAMGzbMREVFme+//96qGTJkiOnbt6/Jy8sz69evN127djWjRo3y6G3Xrl2moKDADB061Pz2t781BQUFpqCgwJp/9dVXjZ+fn5k3b575+uuvrUdpaalVs3nzZuPn52cef/xxs2/fPvPqq6+a5s2bm3/+85+NXiO+TQkAwLnHm8/vRoexF1980TgcDtOmTRvj4+Nj2rZta/7xj3+YkJAQc8cdd1i3uPDWc889Zzp27Gj8/f1NbGys2bRpkzU3cOBAk5aW5lGflZVlunXrZvz9/U2vXr3MO++84zHvdrvNlClTTHh4uHE6nSYhIcHs3bvXo+bIkSNm1KhRpkWLFsblcpn09HRTUVHhUdOpUycjqd7jp701NP/zft9++23Tu3dv43Q6Tffu3c2CBQu8Wh/CGAAA5x5vPr8dxhjTmCNol112mW6++WY98MADev3113X99dfr17/+tbKystS+ffumP2QHST+eZg0ODlZZWZlcLpfd7QAAgEbw5vO70WEsKChIu3btUufOnWWMkdPp1Jo1a9S/f/8maRoNI4wBAHDu8ebzu9EX8H///fdq3ry5JMnhcMjpdHp8YxEAAADe8+rWFi+++KJatGgh6cfbQSxcuFChoaEeNRMmTGi67gAAAM5zjT5N2blzZzkcjhNvzOHQp59+2iSN4UecpgQA4Nzjzed3o4+MffbZZ7+0LwAAAPxMk930FQAAAN4jjAEAANiIMAYAAGAjwhgAAICNCGMAAAA28uo+Y9KPX9VsSN2NYP39/X9xUwAAABcKr8NYSEjICe831r59e40ePVrTpk2Tjw8H3gAAAE7E6zC2cOFCPfzwwxo9erRiY2MlSZs3b9Yrr7yiRx55RN98841mzZolp9OpP/7xj03eMAAAwPnE6zD2yiuvaPbs2Ro5cqQ1NnToUEVHR+uFF15QTk6OOnbsqMcff5wwBgAAcBJen0fcuHGj+vbtW2+8b9++ys3NlSRdeeWV+uKLL355dwAAAOc5r4+MdejQQS+99JKefPJJj/GXXnpJHTp0kCQdOXJErVq1apoOcVrUHHPrlY37tXl/ib6rPqbWQf46crRGVcdqFeDnqzZePHf6+sjhcJzSa5tyW2dLH+zTudEH+3Ru9ME+sT6nc59CWzjl4+PQRa0C9Zsuofr1xW3k63Pi38N9OngdxmbNmqXrr79eK1eu1BVXXCFJ2rp1q/bs2aPXXntNkrRlyxbdcMMNTdspmkzGikItWLdfjfoN8QAAXADmrflEIc2b6cnrojWkd7sz+t4OY4zXn8n79+/XCy+8oI8++kiSdOmll+qOO+5Q586dm7q/C543v/W9MTJWFOqFdfuboDMAAM5P82/61S8OZN58fp9SGMOZ05RhrOaYW5c+spIjYgAAnEC74ACt/9+rftEpS28+v70+TSlJpaWl2rx5sw4dOiS32+0xd8stt5zKJnEG/CP3M4IYAAAn8XVZlTbvL1F8lzZn5P28DmNvv/22UlNTVVlZKZfL5XEDWIfDQRg7i31e8p3dLQAAcE44VFF1xt7L61tb3H///br11ltVWVmp0tJSffvtt9ajpKTkdPSIJtKpdXO7WwAA4JwQ1jLgjL2X12Hs4MGDmjBhgpo354P9XHNzfGed+S/sAgBwbmkXHKDYqNZn7P28DmNJSUnaunXr6egFp5m/n4/G/leU3W0AAHBWmza05xm935jX14wlJyfrgQceUGFhoaKjo9WsWTOP+d///vdN1hya3uRre0oS9xkDAOBnWjVvpoxz4T5jPj7HP5jmcDhUW1v7i5vC/9PU9xmrwx34z41tnS19sE/nRh/s07nRx9m6rbOlj/PlDvzcZ+w8crrCGAAAOH28+fz2+poxAAAANJ1GXTM2d+5cjR07VgEBAZo7d+4JaydMmNAkjQEAAFwIGnWaMioqSlu3blWbNm0UFXX8b+M5HA59+umnTdrghY7TlAAAnHua/Nch7d+/v8G/AwAA4JfhmjEAAAAbeX2fsdraWi1cuFA5OTkN/qLw1atXN1lzAAAA5zuvw9jEiRO1cOFCJScnq3fv3h6/KBwAAADe8TqMLV68WFlZWbr22mtPRz8AAAAXFK+vGfP399cll1xyOnoBAAC44Hgdxu6//349++yz4sb9AAAAv5zXpynXr1+vNWvWaOXKlerVq1e9XxS+bNmyJmsOAADgfOd1GAsJCdHw4cNPRy8AAAAXHK/C2LFjxzRo0CBdffXVioiIOF09AQAAXDC8umbMz89Pd955p6qrq09XPwAAABcUry/gj42NVUFBwenoBQAA4ILj9TVjd999t+6//359+eWXiomJUVBQkMf8ZZdd1mTNAQAAnO+8PjJ24403av/+/ZowYYL69++vyy+/XH379rX+PBXz5s1T586dFRAQoLi4OG3evPmE9UuXLlX37t0VEBCg6OhorVixwmPeGKOpU6eqXbt2CgwMVGJiovbt2+dRU1JSotTUVLlcLoWEhGjMmDGqrKy05quqqjR69GhFR0fLz89PKSkp9fpYtmyZBg8erLZt28rlcik+Pl6rVq06bt9PPvmkHA6H7r333pMvCgAAuCB4Hcb2799f7/Hpp59af3pryZIlmjRpkqZNm6Zt27apT58+SkpK0qFDhxqs37hxo0aNGqUxY8aooKBAKSkpSklJ0c6dO62aGTNmaO7cuZo/f77y8vIUFBSkpKQkVVVVWTWpqanatWuXsrOztXz5cq1bt05jx4615mtraxUYGKgJEyYoMTGxwV7WrVunwYMHa8WKFcrPz9egQYM0dOjQBk/jbtmyRS+88AJHDgEAgCdjs9jYWDNu3DjreW1trYmMjDQZGRkN1o8cOdIkJyd7jMXFxZk77rjDGGOM2+02ERERZubMmdZ8aWmpcTqdJjMz0xhjTGFhoZFktmzZYtWsXLnSOBwOc/DgwXrvmZaWZoYNG9ao/enZs6eZPn26x1hFRYXp2rWryc7ONgMHDjQTJ05s1LaMMaasrMxIMmVlZY1+DQAAsJc3n99eHxmrU1hYqHfffVdvvfWWx8MbNTU1ys/P9zjy5OPjo8TEROXm5jb4mtzc3HpHqpKSkqz6/fv3q6ioyKMmODhYcXFxVk1ubq5CQkLUr18/qyYxMVE+Pj7Ky8vzah9+yu12q6KiQq1bt/YYHzdunJKTk497hO2nqqurVV5e7vEAAADnL68v4P/00081fPhw7dixQw6Hw/q1SA6HQ9KPp/ca6/Dhw6qtrVV4eLjHeHh4uPbs2dPga4qKihqsLyoqsubrxk5UExYW5jHv5+en1q1bWzWnYtasWaqsrNTIkSOtscWLF2vbtm3asmVLo7aRkZGh6dOnn3IPAADg3OL1kbGJEycqKipKhw4dUvPmzbVr1y6tW7dO/fr103vvvXcaWjw3LFq0SNOnT1dWVpYV9A4cOKCJEyfq1VdfVUBAQKO2M3nyZJWVlVmPAwcOnM62AQCAzbw+Mpabm6vVq1crNDRUPj4+8vHx0ZVXXqmMjAxNmDDBq3uQhYaGytfXV8XFxR7jxcXFx73Df0RExAnr6/4sLi5Wu3btPGouv/xyq+bnXxA4duyYSkpKTuk3CyxevFi33Xabli5d6nEqMj8/X4cOHdKvfvUra6y2tlbr1q3Tn//8Z1VXV8vX19djW06nU06n0+seAADAucnrI2O1tbVq2bKlpB/D1FdffSVJ6tSpk/bu3evVtvz9/RUTE6OcnBxrzO12KycnR/Hx8Q2+Jj4+3qNekrKzs636qKgoRUREeNSUl5crLy/PqomPj1dpaany8/OtmtWrV8vtdisuLs6rfcjMzFR6eroyMzOVnJzsMZeQkKAdO3Zo+/bt1qNfv35KTU3V9u3b6wUxAABw4fH6yFjv3r31wQcfKCoqSnFxcZoxY4b8/f21YMECXXzxxV43MGnSJKWlpalfv36KjY3VnDlzdPToUaWnp0uSbrnlFl100UXKyMiQ9ONp0oEDB2r27NlKTk7W4sWLtXXrVi1YsECSrPt4PfbYY+ratauioqI0ZcoURUZGWvcK69Gjh4YMGaLbb79d8+fP1w8//KDx48frxhtvVGRkpNVbYWGhampqVFJSooqKCm3fvl2SrCNsixYtUlpamp599lnFxcVZ15sFBgYqODhYLVu2VO/evT32NygoSG3atKk3DgAALlDeflXz3XffNa+//roxxph9+/aZSy+91DgcDhMaGmpycnK8/uqnMcY899xzpmPHjsbf39/ExsaaTZs2WXMDBw40aWlpHvVZWVmmW7duxt/f3/Tq1cu88847HvNut9tMmTLFhIeHG6fTaRISEszevXs9ao4cOWJGjRplWrRoYVwul0lPTzcVFRUeNZ06dTKS6j1+2ltD8z/v96e4tQUAAOc/bz6/Hcb8/1+H/AVKSkrUqlUr6xuVaDrl5eUKDg5WWVmZXC6X3e0AAIBG8Obz+5TvM/bxxx9r1apV+v777+vdVwsAAACN43UYO3LkiBISEtStWzdde+21+vrrryVJY8aM0f3339/kDQIAAJzPvA5j9913n5o1a6YvvvhCzZs3t8ZvuOEGvfvuu03aHAAAwPnO629T/vvf/9aqVavUvn17j/GuXbvq888/b7LGAAAALgReHxk7evSoxxGxOiUlJdysFAAAwEteh7EBAwbo73//u/Xc4XDI7XZrxowZGjRoUJM2BwAAcL7z+jTljBkzlJCQoK1bt6qmpkYPPvigdu3apZKSEm3YsOF09AgAAHDe8vrIWO/evfXRRx/pyiuv1LBhw3T06FFdd911KigoUJcuXU5HjwAAAOctr4+MSVJwcLAefvhhj7Evv/xSY8eOtX4tEQAAAE7ulG/6+nNHjhzRSy+91FSbAwAAuCA0WRgDAACA9whjAAAANiKMAQAA2KjRF/Bfd911J5wvLS39pb0AAABccBodxoKDg086f8stt/zihgAAAC4kjQ5jL7/88unsAwAA4ILENWMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADY6KwIY/PmzVPnzp0VEBCguLg4bd68+YT1S5cuVffu3RUQEKDo6GitWLHCY94Yo6lTp6pdu3YKDAxUYmKi9u3b51FTUlKi1NRUuVwuhYSEaMyYMaqsrLTmq6qqNHr0aEVHR8vPz08pKSn1+li2bJkGDx6stm3byuVyKT4+XqtWrfKoycjI0BVXXKGWLVsqLCxMKSkp2rt3r5crBAAAzle2h7ElS5Zo0qRJmjZtmrZt26Y+ffooKSlJhw4darB+48aNGjVqlMaMGaOCggKlpKQoJSVFO3futGpmzJihuXPnav78+crLy1NQUJCSkpJUVVVl1aSmpmrXrl3Kzs7W8uXLtW7dOo0dO9aar62tVWBgoCZMmKDExMQGe1m3bp0GDx6sFStWKD8/X4MGDdLQoUNVUFBg1axdu1bjxo3Tpk2blJ2drR9++EFXX321jh49+kuXDgAAnA+MzWJjY824ceOs57W1tSYyMtJkZGQ0WD9y5EiTnJzsMRYXF2fuuOMOY4wxbrfbREREmJkzZ1rzpaWlxul0mszMTGOMMYWFhUaS2bJli1WzcuVK43A4zMGDB+u9Z1pamhk2bFij9qdnz55m+vTpx50/dOiQkWTWrl3bqO2VlZUZSaasrKxR9QAAwH7efH7bemSspqZG+fn5HkeefHx8lJiYqNzc3AZfk5ubW+9IVVJSklW/f/9+FRUVedQEBwcrLi7OqsnNzVVISIj69etn1SQmJsrHx0d5eXmnvD9ut1sVFRVq3br1cWvKysok6YQ1AADgwuFn55sfPnxYtbW1Cg8P9xgPDw/Xnj17GnxNUVFRg/VFRUXWfN3YiWrCwsI85v38/NS6dWur5lTMmjVLlZWVGjlyZIPzbrdb9957r/r376/evXs3WFNdXa3q6mrreXl5+Sn3AwAAzn62XzN2vli0aJGmT5+urKysekGvzrhx47Rz504tXrz4uNvJyMhQcHCw9ejQocPpahkAAJwFbA1joaGh8vX1VXFxscd4cXGxIiIiGnxNRETECevr/jxZzc+/IHDs2DGVlJQc931PZPHixbrtttuUlZV13Iv9x48fr+XLl2vNmjVq3779cbc1efJklZWVWY8DBw543Q8AADh32BrG/P39FRMTo5ycHGvM7XYrJydH8fHxDb4mPj7eo16SsrOzrfqoqChFRER41JSXlysvL8+qiY+PV2lpqfLz862a1atXy+12Ky4uzqt9yMzMVHp6ujIzM5WcnFxv3hij8ePH64033tDq1asVFRV1wu05nU65XC6PBwAAOH/Zes2YJE2aNElpaWnq16+fYmNjNWfOHB09elTp6emSpFtuuUUXXXSRMjIyJEkTJ07UwIEDNXv2bCUnJ2vx4sXaunWrFixYIElyOBy699579dhjj6lr166KiorSlClTFBkZad0rrEePHhoyZIhuv/12zZ8/Xz/88IPGjx+vG2+8UZGRkVZvhYWFqqmpUUlJiSoqKrR9+3ZJ0uWXXy7px1OTaWlpevbZZxUXF2ddbxYYGKjg4GBJP56aXLRokd588021bNnSqgkODlZgYOBpXVsAAHAOOP1f7jy55557znTs2NH4+/ub2NhYs2nTJmtu4MCBJi0tzaM+KyvLdOvWzfj7+5tevXqZd955x2Pe7XabKVOmmPDwcON0Ok1CQoLZu3evR82RI0fMqFGjTIsWLYzL5TLp6emmoqLCo6ZTp05GUr3HT3traP6n/TY0L8m8/PLLjVobbm0BAMC5x5vPb4cxxpzpAIjGKy8vV3BwsMrKyjhlCQDAOcKbz2++TQkAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPCGAAAgI0IYwAAADYijAEAANiIMAYAAGAjwhgAAICNCGMAAAA2IowBAADYiDAGAABgI8IYAACAjQhjAAAANjorwti8efPUuXNnBQQEKC4uTps3bz5h/dKlS9W9e3cFBAQoOjpaK1as8Jg3xmjq1Klq166dAgMDlZiYqH379nnUlJSUKDU1VS6XSyEhIRozZowqKyut+aqqKo0ePVrR0dHy8/NTSkpKvT6WLVumwYMHq23btnK5XIqPj9eqVat+8f4BAIALh+1hbMmSJZo0aZKmTZumbdu2qU+fPkpKStKhQ4carN+4caNGjRqlMWPGqKCgQCkpKUpJSdHOnTutmhkzZmju3LmaP3++8vLyFBQUpKSkJFVVVVk1qamp2rVrl7Kzs7V8+XKtW7dOY8eOteZra2sVGBioCRMmKDExscFe1q1bp8GDB2vFihXKz8/XoEGDNHToUBUUFJzy/gEAgAuMsVlsbKwZN26c9by2ttZERkaajIyMButHjhxpkpOTPcbi4uLMHXfcYYwxxu12m4iICDNz5kxrvrS01DidTpOZmWmMMaawsNBIMlu2bLFqVq5caRwOhzl48GC990xLSzPDhg1r1P707NnTTJ8+/ZT37+fKysqMJFNWVtaoegAAYD9vPr9tPTJWU1Oj/Px8jyNPPj4+SkxMVG5uboOvyc3NrXekKikpyarfv3+/ioqKPGqCg4MVFxdn1eTm5iokJET9+vWzahITE+Xj46O8vLxT3h+3262Kigq1bt36lPevurpa5eXlHg8AAHD+sjWMHT58WLW1tQoPD/cYDw8PV1FRUYOvKSoqOmF93Z8nqwkLC/OY9/PzU+vWrY/7vo0xa9YsVVZWauTIkZJObf8yMjIUHBxsPTp06HDK/QAAgLOf7deMnS8WLVqk6dOnKysrq17Q88bkyZNVVlZmPQ4cONCEXQIAgLONn51vHhoaKl9fXxUXF3uMFxcXKyIiosHXREREnLC+7s/i4mK1a9fOo+byyy+3an5+Af2xY8dUUlJy3Pc9kcWLF+u2227T0qVLPU5Jnsr+OZ1OOZ1Or3sAAADnJluPjPn7+ysmJkY5OTnWmNvtVk5OjuLj4xt8TXx8vEe9JGVnZ1v1UVFRioiI8KgpLy9XXl6eVRMfH6/S0lLl5+dbNatXr5bb7VZcXJxX+5CZman09HRlZmYqOTn5F+8fAAC4wJyBLxSc0OLFi43T6TQLFy40hYWFZuzYsSYkJMQUFRUZY4y5+eabzUMPPWTVb9iwwfj5+ZlZs2aZ3bt3m2nTpplmzZqZHTt2WDVPPvmkCQkJMW+++ab58MMPzbBhw0xUVJT5/vvvrZohQ4aYvn37mry8PLN+/XrTtWtXM2rUKI/edu3aZQoKCszQoUPNb3/7W1NQUGAKCgqs+VdffdX4+fmZefPmma+//tp6lJaWNnr/ToZvUwIAcO7x5vPb9jBmjDHPPfec6dixo/H39zexsbFm06ZN1tzAgQNNWlqaR31WVpbp1q2b8ff3N7169TLvvPOOx7zb7TZTpkwx4eHhxul0moSEBLN3716PmiNHjphRo0aZFi1aGJfLZdLT001FRYVHTadOnYykeo+f9tbQ/M/7PdH+nQxhDACAc483n98OY4yx44gcGqe8vFzBwcEqKyuTy+Wyux0AANAI3nx+821KAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbEQYAwAAsBFhDAAAwEaEMQAAABsRxgAAAGxEGAMAALCRn90N4MSMMZKk8vJymzsBAACNVfe5Xfc5fiKEsbNcRUWFJKlDhw42dwIAALxVUVGh4ODgE9Y4TGMiG2zjdrv11VdfqWXLlnI4HE223fLycnXo0EEHDhyQy+Vqsu1eCFi7U8fanTrW7tSxdqeOtTt1xhhVVFQoMjJSPj4nviqMI2NnOR8fH7Vv3/60bd/lcvEf2Cli7U4da3fqWLtTx9qdOtbu1JzsiFgdLuAHAACwEWEMAADARoSxC5TT6dS0adPkdDrtbuWcw9qdOtbu1LF2p461O3Ws3ZnBBfwAAAA24sgYAACAjQhjAAAANiKMAQAA2IgwBgAAYCPC2AVo3rx56ty5swICAhQXF6fNmzfb3ZLt1q1bp6FDhyoyMlIOh0P/+te/POaNMZo6daratWunwMBAJSYmat++fR41JSUlSk1NlcvlUkhIiMaMGaPKysozuBf2yMjI0BVXXKGWLVsqLCxMKSkp2rt3r0dNVVWVxo0bpzZt2qhFixYaMWKEiouLPWq++OILJScnq3nz5goLC9MDDzygY8eOncldOeOef/55XXbZZdYNNePj47Vy5UprnnVrvCeffFIOh0P33nuvNcb6NezRRx+Vw+HweHTv3t2aZ93OPMLYBWbJkiWaNGmSpk2bpm3btqlPnz5KSkrSoUOH7G7NVkePHlWfPn00b968BudnzJihuXPnav78+crLy1NQUJCSkpJUVVVl1aSmpmrXrl3Kzs7W8uXLtW7dOo0dO/ZM7YJt1q5dq3HjxmnTpk3Kzs7WDz/8oKuvvlpHjx61au677z69/fbbWrp0qdauXauvvvpK1113nTVfW1ur5ORk1dTUaOPGjXrllVe0cOFCTZ061Y5dOmPat2+vJ598Uvn5+dq6dauuuuoqDRs2TLt27ZLEujXWli1b9MILL+iyyy7zGGf9jq9Xr176+uuvrcf69eutOdbNBgYXlNjYWDNu3DjreW1trYmMjDQZGRk2dnV2kWTeeOMN67nb7TYRERFm5syZ1lhpaalxOp0mMzPTGGNMYWGhkWS2bNli1axcudI4HA5z8ODBM9b72eDQoUNGklm7dq0x5se1atasmVm6dKlVs3v3biPJ5ObmGmOMWbFihfHx8TFFRUVWzfPPP29cLpeprq4+sztgs1atWpkXX3yRdWukiooK07VrV5OdnW0GDhxoJk6caIzh5+5Epk2bZvr06dPgHOtmD46MXUBqamqUn5+vxMREa8zHx0eJiYnKzc21sbOz2/79+1VUVOSxbsHBwYqLi7PWLTc3VyEhIerXr59Vk5iYKB8fH+Xl5Z3xnu1UVlYmSWrdurUkKT8/Xz/88IPH+nXv3l0dO3b0WL/o6GiFh4dbNUlJSSovL7eOEp3vamtrtXjxYh09elTx8fGsWyONGzdOycnJHusk8XN3Mvv27VNkZKQuvvhipaam6osvvpDEutmFXxR+ATl8+LBqa2s9/gOSpPDwcO3Zs8emrs5+RUVFktTgutXNFRUVKSwszGPez89PrVu3tmouBG63W/fee6/69++v3r17S/pxbfz9/RUSEuJR+/P1a2h96+bOZzt27FB8fLyqqqrUokULvfHGG+rZs6e2b9/Oup3E4sWLtW3bNm3ZsqXeHD93xxcXF6eFCxfq0ksv1ddff63p06drwIAB2rlzJ+tmE8IYgCYzbtw47dy50+P6E5zYpZdequ3bt6usrEyvvfaa0tLStHbtWrvbOusdOHBAEydOVHZ2tgICAuxu55xyzTXXWH+/7LLLFBcXp06dOikrK0uBgYE2dnbh4jTlBSQ0NFS+vr71vhVTXFysiIgIm7o6+9WtzYnWLSIiot6XII4dO6aSkpILZm3Hjx+v5cuXa82aNWrfvr01HhERoZqaGpWWlnrU/3z9Glrfurnzmb+/vy655BLFxMQoIyNDffr00bPPPsu6nUR+fr4OHTqkX/3qV/Lz85Ofn5/Wrl2ruXPnys/PT+Hh4axfI4WEhKhbt276+OOP+bmzCWHsAuLv76+YmBjl5ORYY263Wzk5OYqPj7exs7NbVFSUIiIiPNatvLxceXl51rrFx8ertLRU+fn5Vs3q1avldrsVFxd3xns+k4wxGj9+vN544w2tXr1aUVFRHvMxMTFq1qyZx/rt3btXX3zxhcf67dixwyPQZmdny+VyqWfPnmdmR84Sbrdb1dXVrNtJJCQkaMeOHdq+fbv16Nevn1JTU62/s36NU1lZqU8++UTt2rXj584udn+DAGfW4sWLjdPpNAsXLjSFhYVm7NixJiQkxONbMReiiooKU1BQYAoKCowk8/TTT5uCggLz+eefG2OMefLJJ01ISIh58803zYcffmiGDRtmoqKizPfff29tY8iQIaZv374mLy/PrF+/3nTt2tWMGjXKrl06Y+666y4THBxs3nvvPfP1119bj++++86qufPOO03Hjh3N6tWrzdatW018fLyJj4+35o8dO2Z69+5trr76arN9+3bz7rvvmrZt25rJkyfbsUtnzEMPPWTWrl1r9u/fbz788EPz0EMPGYfDYf79738bY1g3b/3025TGsH7Hc//995v33nvP7N+/32zYsMEkJiaa0NBQc+jQIWMM62YHwtgF6LnnnjMdO3Y0/v7+JjY21mzatMnulmy3Zs0aI6neIy0tzRjz4+0tpkyZYsLDw43T6TQJCQlm7969Hts4cuSIGTVqlGnRooVxuVwmPT3dVFRU2LA3Z1ZD6ybJvPzyy1bN999/b+6++27TqlUr07x5czN8+HDz9ddfe2zns88+M9dcc40JDAw0oaGh5v777zc//PDDGd6bM+vWW281nTp1Mv7+/qZt27YmISHBCmLGsG7e+nkYY/0adsMNN5h27doZf39/c9FFF5kbbrjBfPzxx9Y863bmOYwxxp5jcgAAAOCaMQAAABsRxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAM4BDodD//rXv+xuA8BpQBgDgJMYPXq0HA5HvceQIUPsbg3AecDP7gYA4FwwZMgQvfzyyx5jTqfTpm4AnE84MgYAjeB0OhUREeHxaNWqlaQfTyE+//zzuuaaaxQYGKiLL75Yr732msfrd+zYoauuukqBgYFq06aNxo4dq8rKSo+av/3tb+rVq5ecTqfatWun8ePHe8wfPnxYw4cPV/PmzdW1a1e99dZb1ty3336r1NRUtW3bVoGBgeratWu98Ajg7EQYA4AmMGXKFI0YMUIffPCBUlNTdeONN2r37t2SpKNHjyopKUmtWrXSli1btHTpUv3nP//xCFvPP/+8xo0bp7Fjx2rHjh166623dMkll3i8x/Tp0zVy5Eh9+OGHuvbaa5WamqqSkhLr/QsLC7Vy5Urt3r1bzz//vEJDQ8/cAgA4dXb/pnIAONulpaUZX19fExQU5PF4/PHHjTHGSDJ33nmnx2vi4uLMXXfdZYwxZsGCBaZVq1amsrLSmn/nnXeMj4+PKSoqMsYYExkZaR5++OHj9iDJPPLII9bzyspKI8msXLnSGGPM0KFDTXp6etPsMIAzimvGAKARBg0apOeff95jrHXr1tbf4+PjPebi4+O1fft2SdLu3bvVp08fBQUFWfP9+/eX2+3W3r175XA49NVXXykhIeGEPVx22WXW34OCguRyuXTo0CFJ0l133aURI0Zo27Ztuvrqq5WSkqLf/OY3p7SvAM4swhgANEJQUFC904ZNJTAwsFF1zZo183jucDjkdrslSddcc40+//xzrVixQtnZ2UpISNC4ceM0a9asJu8XQNPimjEAaAKbNm2q97xHjx6SpB49euiDDz7Q0aNHrfkNGzbIx8dHl156qVq2bKnOnTsrJyfnF/XQtm1bpaWl6Z///KfmzJmjBQsW/KLtATgzODIGAI1QXV2toqIijzE/Pz/rIvmlS5eqX79+uvLKK/Xqq69q8+bNeumllyRJqampmjZtmtLS0vToo4/qm2++0T333KObb75Z4eHhkqRHH31Ud955p8LCwnTNNdeooqJCGzZs0D333NOo/qZOnaqYmBj16tVL1dXVWr58uRUGAZzdCGMA0Ajvvvuu2rVr5zF26aWXas+ePZJ+/Kbj4sWLdffdd6tdu3bKzMxUz549JUnNmzfXqlWrNHHiRF1xxRVq3ry5RowYoaefftraVlpamqqqqvTMM8/of/7nfxQaGqr//u//bnR//v7+mjx5sj777DMFBgZqwIABWrx4cRPsOYDTzWGMMXY3AQDnMofDoTfeeEMpKSl2twLgHMQ1YwAAADYijAEAANiIa8YA4Bfiag8AvwRHxgAAAGxEGAMAALARYQwAAMBGhDEAAAAbEcYAAABsRBgDAACwEWEMAADARoQxAAAAGxHGAAAAbPT/AYR4y1aCgl+/AAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAA9hAAAPYQGoP6dpAABt+UlEQVR4nO3dd3hU1dbH8e9MyqRPCBBCIITeq4AIKnoFqXJVUKQoVbkqeEXEgqiIDSu2q2Kl+IoC9iuKIlUloiCg9HIhIBASCOmkznn/GDMwpJBJJpmU3+d58pg5LWsOkVnss/ZeJsMwDERERERqELOnAxARERGpaEqAREREpMZRAiQiIiI1jhIgERERqXGUAImIiEiNowRIREREahwlQCIiIlLjKAESERGRGkcJkIiIiNQ4SoBEpFpasGABJpOJQ4cOObZdeeWVXHnllR6L6XyFxVgR1q5di8lkYu3atRX6c0UqEyVAIm5mMplK9FXdP3waN27s9H7Dw8O5/PLL+fzzzz0dmksyMjJ47LHHPPbn1bFjRxo1akRxXYsuvfRS6tWrR25ubgVGJlK1eXs6AJHq5oMPPnB6vWjRIlauXFlge5s2bSoyLI/o3Lkz9957LwDHjh3jrbfeYujQobz55pvcfvvtFR7P999/7/I5GRkZzJ49G8Ajo0ejR4/mwQcf5Mcff6R3794F9h86dIiYmBimTJmCt7f+ShcpKf3fIuJmN998s9PrX375hZUrVxbYfr6MjAwCAgLKM7QK16BBA6f3PWbMGJo3b85LL71UZAKUm5uLzWbD19fX7fGUxzXL26hRo5gxYwaLFy8uNAH66KOPMAyD0aNHeyA6kapLj8BEPODKK6+kffv2bN68md69exMQEMBDDz0E2B+hPfbYYwXOady4MePGjXPalpSUxNSpU4mKisJisdC8eXOeffZZbDZbsT//mmuuoWnTpoXu69mzJ926dXO8XrlyJZdddhmhoaEEBQXRqlUrR6yuioiIoE2bNhw8eBCwj16YTCZeeOEFXn75ZZo1a4bFYmHnzp0A7N69mxtuuIGwsDD8/Pzo1q0bX331VYHr7tixg6uuugp/f38aNmzIk08+Weg9KKwGKDMzk8cee4yWLVvi5+dH/fr1GTp0KAcOHODQoUPUrVsXgNmzZzse55375+PuGM8XFRVF7969+eSTT8jJySmwf/HixTRr1owePXoQGxvLnXfeSatWrfD396d27drceOONJaoxKuz3Cwq/Z1lZWcyaNYvmzZtjsViIiori/vvvJysry+k4d/7uiLibRoBEPOTUqVMMHDiQESNGcPPNN1OvXj2Xzs/IyOCKK67g6NGj/Otf/6JRo0Zs2LCBGTNmcPz4cV5++eUiz73pppsYM2YMv/32G927d3dsj42N5ZdffuH5558H7B/a11xzDR07duTxxx/HYrGwf/9+fv7551K955ycHI4cOULt2rWdts+fP5/MzEwmTZqExWIhLCyMHTt2cOmll9KgQQMefPBBAgMDWbp0Kddddx2ffvop119/PQBxcXH84x//IDc313Hc22+/jb+//wXjycvL45prrmHVqlWMGDGCu+++m9TUVFauXMn27dvp27cvb775JnfccQfXX389Q4cOBex1Ofn3p7xjBPtjsEmTJvHdd99xzTXXOLb/+eefbN++nUcffRSA3377jQ0bNjBixAgaNmzIoUOHePPNN7nyyivZuXOnW0YYbTYb//znP/npp5+YNGkSbdq04c8//+Sll15i7969fPHFF457487fHRG3M0SkXE2ePNk4/3+1K664wgCMefPmFTgeMGbNmlVge3R0tDF27FjH6yeeeMIIDAw09u7d63Tcgw8+aHh5eRmHDx8uMqbk5GTDYrEY9957r9P25557zjCZTEZsbKxhGIbx0ksvGYCRkJBwobdZaLz9+vUzEhISjISEBGPbtm3GiBEjDMC46667DMMwjIMHDxqAERISYsTHxzud36dPH6NDhw5GZmamY5vNZjN69epltGjRwrFt6tSpBmBs3LjRsS0+Pt6wWq0GYBw8eNCx/YorrjCuuOIKx+v333/fAIy5c+cWiN9msxmGYRgJCQlF/pmUR4yFSUxMNCwWizFy5Ein7Q8++KABGHv27DEMwzAyMjIKnBsTE2MAxqJFixzb1qxZYwDGmjVrHNvO//3Kd/49++CDDwyz2Wz8+OOPTsfNmzfPAIyff/7ZMIyy/e6IVAQ9AhPxEIvFwvjx40t9/rJly7j88supVasWJ0+edHz17duXvLw81q9fX+S5ISEhDBw4kKVLlzrNLlqyZAmXXHIJjRo1AiA0NBSAL7/8skSPa873/fffU7duXerWrUunTp1YtmwZt9xyC88++6zTccOGDXM8agJITExk9erVDB8+nNTUVMd7O3XqFP3792ffvn0cPXoUgG+++YZLLrmEiy++2HF+3bp1S1QT8+mnn1KnTh3uuuuuAvtMJlOx51ZUjAC1atVi0KBBfPXVV6SnpwNgGAYff/wx3bp1o2XLlgBOI0o5OTmcOnWK5s2bExoayu+//16in3Uhy5Yto02bNrRu3drp9+6qq64CYM2aNUDZf3dEypsSIBEPadCgQZmKcvft28eKFSscCUb+V9++fQGIj48v9vybbrqJI0eOEBMTA8CBAwfYvHkzN910k9Mxl156Kbfeeiv16tVjxIgRLF26tMQfaD169GDlypX88MMPbNiwgZMnT7Jo0aICj36aNGni9Hr//v0YhsEjjzxS4P3NmjXL6f3FxsbSokWLAj+7VatWF4zvwIEDtGrVqlSzpyoqxnyjR48mPT2dL7/8EoANGzZw6NAhpyTqzJkzPProo46asDp16lC3bl2SkpJITk52+T0WZt++fezYsaPAe85PwvLfc1l/d0TKm2qARDykpPUf+fLy8pxe22w2rr76au6///5Cj8//QCrKkCFDCAgIYOnSpfTq1YulS5diNpu58cYbnWJcv349a9asYfny5axYsYIlS5Zw1VVX8f333+Pl5VXsz6hTp44jISvO+fci/0Ny+vTp9O/fv9BzmjdvfsHrlqeKjvGaa67BarWyePFiRo0axeLFi/Hy8mLEiBGOY+666y7mz5/P1KlT6dmzJ1arFZPJxIgRIy6YeBQ14pWXl+f052yz2ejQoQNz584t9PioqCig7L87IuVNCZBIJVOrVi2SkpKctmVnZ3P8+HGnbc2aNSMtLa1ECUZhAgMDueaaa1i2bBlz585lyZIlXH755URGRjodZzab6dOnD3369GHu3Lk8/fTTzJw5kzVr1pT6Z19I/gw1Hx+fC/6M6Oho9u3bV2D7nj17LvhzmjVrxsaNG8nJycHHx6fQY4pKDCoqxnwWi4UbbriBRYsWceLECZYtW8ZVV11FRESE45hPPvmEsWPH8uKLLzq2ZWZmFvh9Kkxhv3dgH706d8Zgs2bN2LZtG3369LngY0JP/O6IlJQegYlUMs2aNStQv/P2228XGAEaPnw4MTExfPfddwWukZSUVKJVgW+66SaOHTvGu+++y7Zt25wef4G9zuV8nTt3Bigw5dmdwsPDufLKK3nrrbcKJH4ACQkJju8HDRrEL7/8wq+//uq0/8MPP7zgzxk2bBgnT57kP//5T4F9+bVR+TOnzk8OKirGc40ePZqcnBz+9a9/kZCQUKCGyMvLq8CK0a+99lqB353CNGvWjF9++YXs7GzHtq+//pojR444HTd8+HCOHj3KO++8U+AaZ86ccdQoeep3R6SkNAIkUsnceuut3H777QwbNoyrr76abdu28d1331GnTh2n4+677z6++uorrrnmGsaNG0fXrl1JT0/nzz//5JNPPuHQoUMFzjnfoEGDCA4OZvr06Xh5eTFs2DCn/Y8//jjr169n8ODBREdHEx8fzxtvvEHDhg257LLL3P7ez/X6669z2WWX0aFDB2677TaaNm3KiRMniImJ4a+//mLbtm0A3H///XzwwQcMGDCAu+++2zHFPDo6mj/++KPYnzFmzBgWLVrEtGnT+PXXX7n88stJT0/nhx9+4M477+Taa6/F39+ftm3bsmTJElq2bElYWBjt27enffv2FRLjua644goaNmzIl19+ib+/v2Nafr5rrrmGDz74AKvVStu2bYmJieGHH34osOxAYW699VY++eQTBgwYwPDhwzlw4AD/93//R7NmzZyOu+WWW1i6dCm33347a9as4dJLLyUvL4/du3ezdOlSvvvuO7p16+bR3x2REvHkFDSRmqCoafDt2rUr9Pi8vDzjgQceMOrUqWMEBAQY/fv3N/bv31/oNOXU1FRjxowZRvPmzQ1fX1+jTp06Rq9evYwXXnjByM7OLlF8o0ePNgCjb9++BfatWrXKuPbaa43IyEjD19fXiIyMNEaOHFlg6n1hoqOjjcGDBxd7TP40+Oeff77Q/QcOHDDGjBljREREGD4+PkaDBg2Ma665xvjkk0+cjvvjjz+MK664wvDz8zMaNGhgPPHEE8Z77713wWnwhmGfOj5z5kyjSZMmho+PjxEREWHccMMNxoEDBxzHbNiwwejatavh6+tbYEq8u2O8kPvuu88AjOHDhxfYd/r0aWP8+PFGnTp1jKCgIKN///7G7t27C/zuFDYN3jAM48UXXzQaNGhgWCwW49JLLzU2bdpU6D3Lzs42nn32WaNdu3aGxWIxatWqZXTt2tWYPXu2kZycbBhG2X53RCqCyTCK6bAnIiIiUg2pBkhERERqHCVAIiIiUuMoARIREZEaRwmQiIiI1DhKgERERKTGUQIkIiIiNY4WQsTe2+bYsWMEBwdfcGl3ERERqRwMwyA1NZXIyEjMZtfGdJQAAceOHXM08BMREZGq5ciRIzRs2NClc5QAAcHBwYD9BoaEhHg4GhERESmJlJQUoqKiHJ/jrvBoAjRnzhw+++wzdu/ejb+/P7169eLZZ5+lVatWjmOuvPJK1q1b53Tev/71L+bNm+d4ffjwYe644w7WrFlDUFAQY8eOZc6cOXh7l+zt5T/2CgkJUQIkIiJSxZSmfMWjCdC6deuYPHky3bt3Jzc3l4ceeoh+/fqxc+dOAgMDHcfddtttPP74447X+d2ZAfLy8hg8eDARERFs2LCB48ePM2bMGHx8fHj66acr9P2IiIhI1VCpeoElJCQQHh7OunXr6N27N2AfAercuTMvv/xyoed8++23XHPNNRw7dox69eoBMG/ePB544AESEhLw9fW94M9NSUnBarWSnJysESAREZEqoiyf35VqGnxycjIAYWFhTts//PBD6tSpQ/v27ZkxYwYZGRmOfTExMXTo0MGR/AD079+flJQUduzYUejPycrKIiUlxelLREREao5KUwRts9mYOnUql156Ke3bt3dsHzVqFNHR0URGRvLHH3/wwAMPsGfPHj777DMA4uLinJIfwPE6Li6u0J81Z84cZs+eXU7vRERERCq7SpMATZ48me3bt/PTTz85bZ80aZLj+w4dOlC/fn369OnDgQMHaNasWal+1owZM5g2bZrjdX4VuYiIiNQMleIR2JQpU/j6669Zs2bNBefx9+jRA4D9+/cDEBERwYkTJ5yOyX8dERFR6DUsFotjxpdmfomIiNQ8Hk2ADMNgypQpfP7556xevZomTZpc8JytW7cCUL9+fQB69uzJn3/+SXx8vOOYlStXEhISQtu2bcslbhEREanaPPoIbPLkySxevJgvv/yS4OBgR82O1WrF39+fAwcOsHjxYgYNGkTt2rX5448/uOeee+jduzcdO3YEoF+/frRt25ZbbrmF5557jri4OB5++GEmT56MxWLx5NsTERGRSsqj0+CLWrho/vz5jBs3jiNHjnDzzTezfft20tPTiYqK4vrrr+fhhx92emwVGxvLHXfcwdq1awkMDGTs2LE888wzJV4IUdPgRUREqp6yfH5XqnWAPEUJkIiISNVTbdYBEhEREakISoBERESkxlECJCIiIjWOEiARERGpcZQAiYiIiHsZBuzc6ekoiqUE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f/7plHyuXLmSkJAQ2rZtWzFvpAq50D0vzNatWwGcftfLfM9LWbQtF/Dxxx8bFovFWLBggbFz505j0qRJRmhoqFPFupTevffea6xdu9Y4ePCg8fPPPxt9+/Y16tSpY8THxxuGYRi333670ahRI2P16tXGpk2bjJ49exo9e/b0cNRVS2pqqrFlyxZjy5YtBmDMnTvX2LJlixEbG2sYhmE888wzRmhoqPHll18af/zxh3HttdcaTZo0Mc6cOeO4xoABA4wuXboYGzduNH766SejRYsWxsiRIz31liq94u55amqqMX36dCMmJsY4ePCg8cMPPxgXXXSR0aJFCyMzM9NxDd1z19xxxx2G1Wo11q5daxw/ftzxlZGR4TjmQn+f5ObmGu3btzf69etnbN261VixYoVRt25dY8aMGZ54S5Xehe75/v37jccff9zYtGmTcfDgQePLL780mjZtavTu3dtxDXfccyVA5ei1114zGjVqZPj6+hoXX3yx8csvv3g6pGrjpptuMurXr2/4+voaDRo0MG666SZj//79jv1nzpwx7rzzTqNWrVpGQECAcf311xvHjx/3YMRVz5o1awygwNfYsWMNw7BPhX/kkUeMevXqGRaLxejTp4+xZ88ep2ucOnXKGDlypBEUFGSEhIQY48ePN1JTUz3wbqqG4u55RkaG0a9fP6Nu3bqGj4+PER0dbdx2220F/lGle+6awu43YMyfP99xTEn+Pjl06JAxcOBAw9/f36hTp45x7733Gjk5ORX8bqqGC93zw4cPG7179zbCwsIMi8ViNG/e3LjvvvuM5ORkp+uU9Z6b/g5GREREpMZQDZCIiIjUOEqAREREpMZRAiQiIiI1jhIgERERqXGUAImIiEiNowRIREREahwlQCIiIlLjKAESETlP48aNefnllz0dhoiUIyVAIlJmJpOp2K/HHnusQuLo0KEDt99+e6H7PvjgAywWCydPnqyQWESkclMCJCJldvz4ccfXyy+/TEhIiNO26dOnO441DIPc3NxyiWPixIl8/PHHnDlzpsC++fPn889//pM6deqUy88WkapFCZCIlFlERITjy2q1YjKZHK93795NcHAw3377LV27dsVisfDTTz8xbtw4rrvuOqfrTJ06lSuvvNLx2mazMWfOHJo0aYK/vz+dOnXik08+KTKOm2++mTNnzvDpp586bT948CBr165l4sSJHDhwgGuvvZZ69eoRFBRE9+7d+eGHH4q85qFDhzCZTI5u1ABJSUmYTCbWrl3r2LZ9+3YGDhxIUFAQ9erV45ZbbnEabfrkk0/o0KED/v7+1K5dm759+5Kenl78jRWRcqMESEQqxIMPPsgzzzzDrl276NixY4nOmTNnDosWLWLevHns2LGDe+65h5tvvpl169YVenydOnW49tpref/99522L1iwgIYNG9KvXz/S0tIYNGgQq1atYsuWLQwYMIAhQ4Zw+PDhUr+3pKQkrrrqKrp06cKmTZtYsWIFJ06cYPjw4YB9hGzkyJFMmDCBXbt2sXbtWoYOHYpaMYp4jrenAxCRmuHxxx/n6quvLvHxWVlZPP300/zwww/07NkTgKZNm/LTTz/x1ltvccUVVxR63sSJExk4cCAHDx6kSZMmGIbBwoULGTt2LGazmU6dOtGpUyfH8U888QSff/45X331FVOmTCnVe/vPf/5Dly5dePrppx3b3n//faKioti7dy9paWnk5uYydOhQoqOjAXu9koh4jkaARKRCdOvWzaXj9+/fT0ZGBldffTVBQUGOr0WLFnHgwIEiz7v66qtp2LAh8+fPB2DVqlUcPnyY8ePHA5CWlsb06dNp06YNoaGhBAUFsWvXrjKNAG3bto01a9Y4xdm6dWsADhw4QKdOnejTpw8dOnTgxhtv5J133uH06dOl/nkiUnYaARKRChEYGOj02mw2F3gElJOT4/g+LS0NgOXLl9OgQQOn4ywWS5E/x2w2M27cOBYuXMhjjz3G/Pnz+cc//kHTpk0BmD59OitXruSFF16gefPm+Pv7c8MNN5CdnV3k9QCnWM+NMz/WIUOG8OyzzxY4v379+nh5ebFy5Uo2bNjA999/z2uvvcbMmTPZuHEjTZo0KfK9iEj50QiQiHhE3bp1OX78uNO2cwuN27Zti8Vi4fDhwzRv3tzpKyoqqthrjx8/niNHjvDZZ5/x+eefM3HiRMe+n3/+mXHjxnH99dfToUMHIiIiOHToULFxAk6xnhsnwEUXXcSOHTto3LhxgVjzEz+TycSll17K7Nmz2bJlC76+vnz++efFvg8RKT9KgETEI6666io2bdrEokWL2LdvH7NmzWL79u2O/cHBwUyfPp177rmHhQsXcuDAAX7//Xdee+01Fi5cWOy1mzRpwlVXXcWkSZOwWCwMHTrUsa9FixZ89tlnbN26lW3btjFq1ChsNluR1/L39+eSSy5xFHCvW7eOhx9+2OmYyZMnk5iYyMiRI/ntt984cOAA3333HePHjycvL4+NGzfy9NNPs2nTJg4fPsxnn31GQkICbdq0KeXdE5GyUgIkIh7Rv39/HnnkEe6//366d+9OamoqY8aMcTrmiSee4JFHHmHOnDm0adOGAQMGsHz58hI9Npo4cSKnT59m1KhR+Pn5ObbPnTuXWrVq0atXL4YMGUL//v256KKLir3W+++/T25uLl27dmXq1Kk8+eSTTvsjIyP5+eefycvLo1+/fnTo0IGpU6cSGhqK2WwmJCSE9evXM2jQIFq2bMnDDz/Miy++yMCBA124YyLiTiZD8zBFRESkhtEIkIiIiNQ4SoBERESkxlECJCIiIjWOEiARERGpcZQAiYiISI2jBEhERERqHCVAIiIiUuMoARIREZEaRwmQiIiI1DhKgERERKTGUQIkIiIiNY4SIBEREalx/h/pXg283FQtvQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, LearningRateScheduler\n",
    "from tensorflow.keras.layers import Dense, LSTM, Dropout\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 读取数据\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷B']]\n",
    "\n",
    "# 特征提取\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "\n",
    "# 索引对齐\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# Kennard-Stone算法实现省略，假设函数名为kennard_stone_selection\n",
    "def kennard_stone_selection(x_variables, k):\n",
    "    x_variables = np.array(x_variables)\n",
    "    original_x = x_variables\n",
    "    distance_to_average = ((x_variables - np.tile(x_variables.mean(axis=0), (x_variables.shape[0], 1))) ** 2).sum(axis=1)\n",
    "    max_distance_sample_number = np.where(distance_to_average == np.max(distance_to_average))\n",
    "    max_distance_sample_number = max_distance_sample_number[0][0]\n",
    "    selected_sample_numbers = list()\n",
    "    selected_sample_numbers.append(max_distance_sample_number)\n",
    "    remaining_sample_numbers = np.arange(0, x_variables.shape[0], 1)\n",
    "    x_variables = np.delete(x_variables, selected_sample_numbers, 0)\n",
    "    remaining_sample_numbers = np.delete(remaining_sample_numbers, selected_sample_numbers, 0)\n",
    "    for iteration in range(1, k):\n",
    "        selected_samples = original_x[selected_sample_numbers, :]\n",
    "        min_distance_to_selected_samples = list()\n",
    "        for min_distance_calculation_number in range(0, x_variables.shape[0]):\n",
    "            distance_to_selected_samples = ((selected_samples - np.tile(x_variables[min_distance_calculation_number, :], (selected_samples.shape[0], 1))) ** 2).sum(axis=1)\n",
    "            min_distance_to_selected_samples.append(np.min(distance_to_selected_samples))\n",
    "        max_distance_sample_number = np.where(min_distance_to_selected_samples == np.max(min_distance_to_selected_samples))\n",
    "        max_distance_sample_number = max_distance_sample_number[0][0]\n",
    "        selected_sample_numbers.append(remaining_sample_numbers[max_distance_sample_number])\n",
    "        x_variables = np.delete(x_variables, max_distance_sample_number, 0)\n",
    "        remaining_sample_numbers = np.delete(remaining_sample_numbers, max_distance_sample_number, 0)\n",
    "    return selected_sample_numbers, remaining_sample_numbers\n",
    "\n",
    "# 划分数据集\n",
    "train_indices, test_indices = kennard_stone_selection(pca_features_df.values, 336)\n",
    "X_train, X_test = pca_features_df.iloc[train_indices], pca_features_df.iloc[test_indices]\n",
    "y_train, y_test = targets.iloc[train_indices], targets.iloc[test_indices]\n",
    "\n",
    "# 调整数据形状\n",
    "X_train = np.expand_dims(X_train, axis=2)\n",
    "X_test = np.expand_dims(X_test, axis=2)\n",
    "\n",
    "# 数据标准化\n",
    "scaler = StandardScaler()\n",
    "X_train_scaled = scaler.fit_transform(X_train.reshape(X_train.shape[0], -1)).reshape(X_train.shape)\n",
    "X_test_scaled = scaler.transform(X_test.reshape(X_test.shape[0], -1)).reshape(X_test.shape)\n",
    "\n",
    "# 调整数据形状以匹配LSTM输入要求\n",
    "X_train_scaled = X_train_scaled.reshape(X_train_scaled.shape[0], 1, X_train_scaled.shape[1])  # 形状变为 (samples, timesteps, features)\n",
    "X_test_scaled = X_test_scaled.reshape(X_test_scaled.shape[0], 1, X_test_scaled.shape[1])      # 形状变为 (samples, timesteps, features)\n",
    "\n",
    "# 检查数据形状\n",
    "print(\"X_train_scaled shape:\", X_train_scaled.shape)\n",
    "print(\"X_test_scaled shape:\", X_test_scaled.shape)\n",
    "\n",
    "# 构建LSTM模型\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(128, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(Dropout(0.2))  # 添加 Dropout 层\n",
    "    model.add(LSTM(128))\n",
    "    model.add(Dropout(0.2))  # 添加 Dropout 层\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=1e-3), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "# 创建LSTM模型\n",
    "model_lstm = LSTM_model(X_train_scaled.shape[1:], 1)\n",
    "\n",
    "# 定义回调函数\n",
    "early_stopping = EarlyStopping(monitor='val_loss', patience=100)\n",
    "reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=50, min_lr=1e-5)\n",
    "\n",
    "# 训练模型\n",
    "start_train_time = time.time()\n",
    "history = model_lstm.fit(\n",
    "    X_train_scaled, y_train,\n",
    "    epochs=1000,\n",
    "    batch_size=10,\n",
    "    validation_split=0.2,\n",
    "    verbose=0,\n",
    "    callbacks=[early_stopping, reduce_lr]\n",
    ")\n",
    "end_train_time = time.time()\n",
    "train_time = end_train_time - start_train_time\n",
    "\n",
    "# 获取最佳epoch轮数\n",
    "best_epoch = early_stopping.stopped_epoch + 1  # stopped_epoch 从 0 开始计数\n",
    "print(f\"最佳训练轮数: {best_epoch}\")\n",
    "\n",
    "# 获取学习率历史记录\n",
    "lr_history = []\n",
    "for epoch in range(len(history.epoch)):\n",
    "    lr = model_lstm.optimizer.learning_rate.numpy()\n",
    "    lr_history.append(lr)\n",
    "    print(f\"Epoch {epoch + 1}: 学习率 = {lr:.6f}\")\n",
    "\n",
    "# 测试模型\n",
    "start_test_time = time.time()\n",
    "y_pred_lstm = model_lstm.predict(X_test_scaled)\n",
    "end_test_time = time.time()\n",
    "test_time = end_test_time - start_test_time\n",
    "\n",
    "# 计算性能指标\n",
    "def calculate_metrics(y_true, y_pred):\n",
    "    rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "    r = pearsonr(y_true.ravel(), y_pred.ravel())[0]\n",
    "    return rmse, r\n",
    "\n",
    "rmsec, r_cal = calculate_metrics(y_train.values, model_lstm.predict(X_train_scaled))\n",
    "rmsep, r_val = calculate_metrics(y_test.values, y_pred_lstm)\n",
    "RPD = np.std(y_test.values) / rmsep\n",
    "\n",
    "# 输出性能指标\n",
    "print(f\"RMSEc (校正均方根误差): {rmsec}\\nRMSEp (预测均方根误差): {rmsep}\\nRcal (校正集相关系数): {r_cal}\\nRval (验证集相关系数): {r_val}\\nRPD (相对预测偏差): {RPD}\")\n",
    "print(f\"Training time: {train_time} seconds\")\n",
    "print(f\"Testing time: {test_time} seconds\")\n",
    "\n",
    "# 绘制训练曲线\n",
    "plt.plot(history.history['loss'], label='Training Loss')\n",
    "plt.plot(history.history['val_loss'], label='Validation Loss')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "plt.title('Training and Validation Loss')\n",
    "plt.show()\n",
    "\n",
    "# 绘制学习率变化曲线\n",
    "plt.plot(range(1, len(lr_history) + 1), lr_history, marker='o')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Learning Rate')\n",
    "plt.title('Learning Rate over Epochs')\n",
    "plt.show()\n",
    "\n",
    "# 绘制预测结果对比图\n",
    "plt.scatter(y_test, y_pred_lstm, alpha=0.7)\n",
    "plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color='red', linestyle='--')\n",
    "plt.xlabel('True Values')\n",
    "plt.ylabel('Predicted Values')\n",
    "plt.title('True vs Predicted Values')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "4218fd9b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fold 1\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 121ms/step\n",
      "Fold 1: RMSE = 11.6908, R = 0.9873\n",
      "Fold 2\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m1/3\u001b[0m \u001b[32m━━━━━━\u001b[0m\u001b[37m━━━━━━━━━━━━━━\u001b[0m \u001b[1m0s\u001b[0m 257ms/stepWARNING:tensorflow:5 out of the last 18 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x0000021F707EF100> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 123ms/step\n",
      "Fold 2: RMSE = 9.6008, R = 0.9875\n",
      "Fold 3\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:6 out of the last 19 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x0000021F5D8D4B80> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 122ms/step\n",
      "Fold 3: RMSE = 9.9193, R = 0.9834\n",
      "Fold 4\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 119ms/step\n",
      "Fold 4: RMSE = 24.0455, R = 0.9416\n",
      "Fold 5\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 120ms/step\n",
      "Fold 5: RMSE = 10.7431, R = 0.9897\n",
      "\n",
      "Cross-Validation Results:\n",
      "Average RMSE: 13.1999 ± 5.4710\n",
      "Average R: 0.9779 ± 0.0182\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from sklearn.model_selection import KFold\n",
    "\n",
    "# 读取数据\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷B']]\n",
    "\n",
    "# 特征提取\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "\n",
    "# 索引对齐\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# 构建LSTM模型\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(128, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(LSTM(128))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=1e-3), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "# 调整数据形状\n",
    "X = np.expand_dims(pca_features_df.values, axis=2)  # (samples, features, 1)\n",
    "y = targets.values\n",
    "\n",
    "# 定义交叉验证\n",
    "kf = KFold(n_splits=5, shuffle=True, random_state=42)  # 使用 5 折交叉验证\n",
    "\n",
    "# 性能指标存储\n",
    "rmse_scores = []\n",
    "r_scores = []\n",
    "\n",
    "# 开始交叉验证\n",
    "for fold, (train_indices, val_indices) in enumerate(kf.split(X)):\n",
    "    print(f\"Fold {fold + 1}\")\n",
    "    \n",
    "    # 划分训练集和验证集\n",
    "    X_train, X_val = X[train_indices], X[val_indices]\n",
    "    y_train, y_val = y[train_indices], y[val_indices]\n",
    "    \n",
    "    # 调整数据形状以匹配LSTM输入要求\n",
    "    X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])  # (samples, timesteps, features)\n",
    "    X_val = X_val.reshape(X_val.shape[0], 1, X_val.shape[1])          # (samples, timesteps, features)\n",
    "    \n",
    "    # 检查数据形状\n",
    "    print(\"X_train shape:\", X_train.shape)\n",
    "    print(\"X_val shape:\", X_val.shape)\n",
    "    \n",
    "    # 创建LSTM模型\n",
    "    model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "    \n",
    "    # 训练模型\n",
    "    early_stopping = EarlyStopping(monitor='val_loss', patience=100)\n",
    "    history = model_lstm.fit(\n",
    "        X_train, y_train,\n",
    "        epochs=1000,\n",
    "        batch_size=10,\n",
    "        validation_data=(X_val, y_val),\n",
    "        verbose=0,\n",
    "        callbacks=[early_stopping]\n",
    "    )\n",
    "    \n",
    "    # 测试模型\n",
    "    y_pred_val = model_lstm.predict(X_val)\n",
    "    \n",
    "    # 计算性能指标\n",
    "    def calculate_metrics(y_true, y_pred):\n",
    "        rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "        r = pearsonr(y_true.ravel(), y_pred.ravel())[0]\n",
    "        return rmse, r\n",
    "    \n",
    "    rmse, r = calculate_metrics(y_val, y_pred_val)\n",
    "    rmse_scores.append(rmse)\n",
    "    r_scores.append(r)\n",
    "    \n",
    "    print(f\"Fold {fold + 1}: RMSE = {rmse:.4f}, R = {r:.4f}\")\n",
    "\n",
    "# 输出交叉验证结果\n",
    "print(\"\\nCross-Validation Results:\")\n",
    "print(f\"Average RMSE: {np.mean(rmse_scores):.4f} ± {np.std(rmse_scores):.4f}\")\n",
    "print(f\"Average R: {np.mean(r_scores):.4f} ± {np.std(r_scores):.4f}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5557d0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "1. 交叉验证的基本思路\n",
    "将数据划分为 K 个子集（例如 5 折或 10 折）。\n",
    "每次用其中一个子集作为验证集，其余子集作为训练集。\n",
    "训练模型并记录每折的性能指标。\n",
    "最终取所有折的平均值作为模型的性能评估结果。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cff0a3c7",
   "metadata": {},
   "source": [
    "RMSEc 和 RMSEp: 小于目标变量标准差的 10%-20%。\n",
    "\n",
    "Rcal 和 Rval: \n",
    "R\n",
    ">\n",
    "0.95\n",
    "R>0.95 被认为是优秀模型。\n",
    "RPD: \n",
    "RPD\n",
    ">\n",
    "2.5\n",
    "RPD>2.5 被认为是优秀模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "14d2b807",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Fold 1\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 25ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step \n",
      "Fold 1:\n",
      "  RMSEc (校正均方根误差): 5.9857\n",
      "  RMSEp (预测均方根误差): 11.2254\n",
      "  Rcal (校正集相关系数): 0.9958\n",
      "  Rval (验证集相关系数): 0.9883\n",
      "  RPD (相对预测偏差): 6.5240\n",
      "\n",
      "Fold 2\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 26ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step \n",
      "Fold 2:\n",
      "  RMSEc (校正均方根误差): 3.9216\n",
      "  RMSEp (预测均方根误差): 10.1007\n",
      "  Rcal (校正集相关系数): 0.9984\n",
      "  Rval (验证集相关系数): 0.9861\n",
      "  RPD (相对预测偏差): 5.9667\n",
      "\n",
      "Fold 3\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 26ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 4ms/step \n",
      "Fold 3:\n",
      "  RMSEc (校正均方根误差): 1.5998\n",
      "  RMSEp (预测均方根误差): 8.4423\n",
      "  Rcal (校正集相关系数): 0.9998\n",
      "  Rval (验证集相关系数): 0.9882\n",
      "  RPD (相对预测偏差): 6.4634\n",
      "\n",
      "Fold 4\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 28ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 5ms/step \n",
      "Fold 4:\n",
      "  RMSEc (校正均方根误差): 6.7320\n",
      "  RMSEp (预测均方根误差): 23.4976\n",
      "  Rcal (校正集相关系数): 0.9950\n",
      "  Rval (验证集相关系数): 0.9443\n",
      "  RPD (相对预测偏差): 2.9974\n",
      "\n",
      "Fold 5\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\30382\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 25ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 3ms/step \n",
      "Fold 5:\n",
      "  RMSEc (校正均方根误差): 3.4514\n",
      "  RMSEp (预测均方根误差): 11.1814\n",
      "  Rcal (校正集相关系数): 0.9986\n",
      "  Rval (验证集相关系数): 0.9888\n",
      "  RPD (相对预测偏差): 6.6693\n",
      "\n",
      "Cross-Validation Results:\n",
      "Average RMSEc: 4.3381 ± 1.8387\n",
      "Average RMSEp: 12.8895 ± 5.3995\n",
      "Average Rcal: 0.9975 ± 0.0018\n",
      "Average Rval: 0.9791 ± 0.0174\n",
      "Average RPD: 5.7242 ± 1.3837\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from sklearn.model_selection import KFold\n",
    "\n",
    "# 读取数据\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷B']]\n",
    "\n",
    "# 特征提取\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "\n",
    "# 索引对齐\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# 构建LSTM模型\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(128, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(LSTM(128))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=1e-3), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "# 调整数据形状\n",
    "X = np.expand_dims(pca_features_df.values, axis=2)  # (samples, features, 1)\n",
    "y = targets.values\n",
    "\n",
    "# 定义交叉验证\n",
    "kf = KFold(n_splits=5, shuffle=True, random_state=42)  # 使用 5 折交叉验证\n",
    "\n",
    "# 性能指标存储\n",
    "rmsec_scores = []  # 校正均方根误差\n",
    "rmsep_scores = []  # 预测均方根误差\n",
    "rcal_scores = []   # 校正集相关系数\n",
    "rval_scores = []   # 验证集相关系数\n",
    "rpd_scores = []    # 相对预测偏差\n",
    "\n",
    "# 开始交叉验证\n",
    "for fold, (train_indices, val_indices) in enumerate(kf.split(X)):\n",
    "    print(f\"\\nFold {fold + 1}\")\n",
    "    \n",
    "    # 划分训练集和验证集\n",
    "    X_train, X_val = X[train_indices], X[val_indices]\n",
    "    y_train, y_val = y[train_indices], y[val_indices]\n",
    "    \n",
    "    # 调整数据形状以匹配LSTM输入要求\n",
    "    X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])  # (samples, timesteps, features)\n",
    "    X_val = X_val.reshape(X_val.shape[0], 1, X_val.shape[1])          # (samples, timesteps, features)\n",
    "    \n",
    "    # 检查数据形状\n",
    "    print(\"X_train shape:\", X_train.shape)\n",
    "    print(\"X_val shape:\", X_val.shape)\n",
    "    \n",
    "    # 创建LSTM模型\n",
    "    model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "    \n",
    "    # 训练模型（使用早停机制）\n",
    "    early_stopping = EarlyStopping(monitor='val_loss', patience=100, restore_best_weights=True)\n",
    "    history = model_lstm.fit(\n",
    "        X_train, y_train,\n",
    "        epochs=1000,\n",
    "        batch_size=10,\n",
    "        validation_data=(X_val, y_val),\n",
    "        verbose=0,\n",
    "        callbacks=[early_stopping]\n",
    "    )\n",
    "    \n",
    "    # 测试模型\n",
    "    y_pred_train = model_lstm.predict(X_train)\n",
    "    y_pred_val = model_lstm.predict(X_val)\n",
    "    \n",
    "    # 计算性能指标\n",
    "    def calculate_metrics(y_true, y_pred):\n",
    "        rmse = np.sqrt(mean_squared_error(y_true, y_pred))  # RMSE\n",
    "        r = pearsonr(y_true.ravel(), y_pred.ravel())[0]     # 相关系数 R\n",
    "        return rmse, r\n",
    "    \n",
    "    # 校正集指标\n",
    "    rmsec, rcal = calculate_metrics(y_train, y_pred_train)\n",
    "    rmsec_scores.append(rmsec)\n",
    "    rcal_scores.append(rcal)\n",
    "    \n",
    "    # 验证集指标\n",
    "    rmsep, rval = calculate_metrics(y_val, y_pred_val)\n",
    "    rmsep_scores.append(rmsep)\n",
    "    rval_scores.append(rval)\n",
    "    \n",
    "    # RPD（相对预测偏差）\n",
    "    rpd = np.std(y_val) / rmsep\n",
    "    rpd_scores.append(rpd)\n",
    "    \n",
    "    # 打印当前折的指标\n",
    "    print(f\"Fold {fold + 1}:\")\n",
    "    print(f\"  RMSEc (校正均方根误差): {rmsec:.4f}\")\n",
    "    print(f\"  RMSEp (预测均方根误差): {rmsep:.4f}\")\n",
    "    print(f\"  Rcal (校正集相关系数): {rcal:.4f}\")\n",
    "    print(f\"  Rval (验证集相关系数): {rval:.4f}\")\n",
    "    print(f\"  RPD (相对预测偏差): {rpd:.4f}\")\n",
    "\n",
    "# 输出交叉验证结果\n",
    "print(\"\\nCross-Validation Results:\")\n",
    "print(f\"Average RMSEc: {np.mean(rmsec_scores):.4f} ± {np.std(rmsec_scores):.4f}\")\n",
    "print(f\"Average RMSEp: {np.mean(rmsep_scores):.4f} ± {np.std(rmsep_scores):.4f}\")\n",
    "print(f\"Average Rcal: {np.mean(rcal_scores):.4f} ± {np.std(rcal_scores):.4f}\")\n",
    "print(f\"Average Rval: {np.mean(rval_scores):.4f} ± {np.std(rval_scores):.4f}\")\n",
    "print(f\"Average RPD: {np.mean(rpd_scores):.4f} ± {np.std(rpd_scores):.4f}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f35e5c02-c54b-45d1-b290-5fff594bf975",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Fold 1\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 47ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 12ms/step\n",
      "Fold 1:\n",
      "  RMSEc: 3.8764\n",
      "  RMSEp: 7.3899\n",
      "  Rcal: 0.9966\n",
      "  Rval: 0.9903\n",
      "  RPD: 7.1321\n",
      "\n",
      "Fold 2\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 39ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 11ms/step\n",
      "Fold 2:\n",
      "  RMSEc: 9.3805\n",
      "  RMSEp: 9.3688\n",
      "  Rcal: 0.9826\n",
      "  Rval: 0.9751\n",
      "  RPD: 4.4319\n",
      "\n",
      "Fold 3\n",
      "X_train shape: (322, 1, 10)\n",
      "X_val shape: (81, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 38ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 11ms/step\n",
      "Fold 3:\n",
      "  RMSEc: 5.6962\n",
      "  RMSEp: 7.6500\n",
      "  Rcal: 0.9936\n",
      "  Rval: 0.9869\n",
      "  RPD: 6.1750\n",
      "\n",
      "Fold 4\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 39ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 13ms/step\n",
      "Fold 4:\n",
      "  RMSEc: 2.0038\n",
      "  RMSEp: 20.8584\n",
      "  Rcal: 0.9992\n",
      "  Rval: 0.8984\n",
      "  RPD: 2.2507\n",
      "\n",
      "Fold 5\n",
      "X_train shape: (323, 1, 10)\n",
      "X_val shape: (80, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m11/11\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 98ms/step\n",
      "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 16ms/step\n",
      "Fold 5:\n",
      "  RMSEc: 3.0224\n",
      "  RMSEp: 7.1674\n",
      "  Rcal: 0.9980\n",
      "  Rval: 0.9906\n",
      "  RPD: 7.2520\n",
      "\n",
      "Cross-Validation Results:\n",
      "Average RMSEc: 4.7959 ± 2.5929\n",
      "Average RMSEp: 10.4869 ± 5.2436\n",
      "Average Rcal: 0.9940 ± 0.0060\n",
      "Average Rval: 0.9682 ± 0.0354\n",
      "Average RPD: 5.4483 ± 1.8907\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from sklearn.model_selection import KFold\n",
    "\n",
    "# 读取数据（保持不变）\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷A']]\n",
    "\n",
    "# 特征提取（保持不变）\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# 构建LSTM模型（保持不变）\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(256, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(LSTM(256))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=0.005), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "X = np.expand_dims(pca_features_df.values, axis=2)\n",
    "y = targets.values\n",
    "\n",
    "# 修改点：将n_splits参数从5改为10\n",
    "kf = KFold(n_splits=5, shuffle=True, random_state=42)  # 现在使用10折交叉验证\n",
    "\n",
    "# 性能指标存储（保持不变）\n",
    "rmsec_scores = []  \n",
    "rmsep_scores = []  \n",
    "rcal_scores = []   \n",
    "rval_scores = []   \n",
    "rpd_scores = []    \n",
    "\n",
    "# 交叉验证循环（自动适应10折）\n",
    "for fold, (train_indices, val_indices) in enumerate(kf.split(X)):\n",
    "    print(f\"\\nFold {fold + 1}\")\n",
    "    \n",
    "    X_train, X_val = X[train_indices], X[val_indices]\n",
    "    y_train, y_val = y[train_indices], y[val_indices]\n",
    "    \n",
    "    # 调整数据形状（保持不变）\n",
    "    X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])\n",
    "    X_val = X_val.reshape(X_val.shape[0], 1, X_val.shape[1])\n",
    "    \n",
    "    print(\"X_train shape:\", X_train.shape)\n",
    "    print(\"X_val shape:\", X_val.shape)\n",
    "    \n",
    "    model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "    \n",
    "    # 训练参数（保持不变）\n",
    "    early_stopping = EarlyStopping(monitor='val_loss', patience=100, restore_best_weights=True)\n",
    "    history = model_lstm.fit(\n",
    "        X_train, y_train,\n",
    "        epochs=1000,\n",
    "        batch_size=10,\n",
    "        validation_data=(X_val, y_val),\n",
    "        verbose=0,\n",
    "        callbacks=[early_stopping]\n",
    "    )\n",
    "    \n",
    "    # 测试和指标计算（保持不变）\n",
    "    y_pred_train = model_lstm.predict(X_train)\n",
    "    y_pred_val = model_lstm.predict(X_val)\n",
    "    \n",
    "    def calculate_metrics(y_true, y_pred):\n",
    "        rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "        r = pearsonr(y_true.ravel(), y_pred.ravel())[0]\n",
    "        return rmse, r\n",
    "    \n",
    "    rmsec, rcal = calculate_metrics(y_train, y_pred_train)\n",
    "    rmsec_scores.append(rmsec)\n",
    "    rcal_scores.append(rcal)\n",
    "    \n",
    "    rmsep, rval = calculate_metrics(y_val, y_pred_val)\n",
    "    rmsep_scores.append(rmsep)\n",
    "    rval_scores.append(rval)\n",
    "    \n",
    "    rpd = np.std(y_val) / rmsep\n",
    "    rpd_scores.append(rpd)\n",
    "    \n",
    "    print(f\"Fold {fold + 1}:\")\n",
    "    print(f\"  RMSEc: {rmsec:.4f}\")\n",
    "    print(f\"  RMSEp: {rmsep:.4f}\")\n",
    "    print(f\"  Rcal: {rcal:.4f}\")\n",
    "    print(f\"  Rval: {rval:.4f}\")\n",
    "    print(f\"  RPD: {rpd:.4f}\")\n",
    "\n",
    "# 结果输出（保持不变，但会显示10折的结果）\n",
    "print(\"\\nCross-Validation Results:\")\n",
    "print(f\"Average RMSEc: {np.mean(rmsec_scores):.4f} ± {np.std(rmsec_scores):.4f}\")\n",
    "print(f\"Average RMSEp: {np.mean(rmsep_scores):.4f} ± {np.std(rmsep_scores):.4f}\")\n",
    "print(f\"Average Rcal: {np.mean(rcal_scores):.4f} ± {np.std(rcal_scores):.4f}\")\n",
    "print(f\"Average Rval: {np.mean(rval_scores):.4f} ± {np.std(rval_scores):.4f}\")\n",
    "print(f\"Average RPD: {np.mean(rpd_scores):.4f} ± {np.std(rpd_scores):.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "01c04aee",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Fold 1\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 44ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 29ms/step\n",
      "Fold 1:\n",
      "  RMSEc: 8.4338\n",
      "  RMSEp: 7.4070\n",
      "  Rcal: 0.9841\n",
      "  Rval: 0.9921\n",
      "  RPD: 7.6194\n",
      "\n",
      "Fold 2\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 33ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 20ms/step\n",
      "Fold 2:\n",
      "  RMSEc: 5.0767\n",
      "  RMSEp: 6.4877\n",
      "  Rcal: 0.9947\n",
      "  Rval: 0.9913\n",
      "  RPD: 7.4710\n",
      "\n",
      "Fold 3\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 33ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 20ms/step\n",
      "Fold 3:\n",
      "  RMSEc: 10.1571\n",
      "  RMSEp: 12.1645\n",
      "  Rcal: 0.9802\n",
      "  Rval: 0.9633\n",
      "  RPD: 3.6334\n",
      "\n",
      "Fold 4\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 38ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 20ms/step\n",
      "Fold 4:\n",
      "  RMSEc: 9.7948\n",
      "  RMSEp: 7.6606\n",
      "  Rcal: 0.9801\n",
      "  Rval: 0.9812\n",
      "  RPD: 5.1676\n",
      "\n",
      "Fold 5\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 84ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 18ms/step\n",
      "Fold 5:\n",
      "  RMSEc: 4.9752\n",
      "  RMSEp: 6.9353\n",
      "  Rcal: 0.9946\n",
      "  Rval: 0.9920\n",
      "  RPD: 7.8497\n",
      "\n",
      "Fold 6\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 35ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 23ms/step\n",
      "Fold 6:\n",
      "  RMSEc: 3.7934\n",
      "  RMSEp: 5.1416\n",
      "  Rcal: 0.9971\n",
      "  Rval: 0.9898\n",
      "  RPD: 7.0037\n",
      "\n",
      "Fold 7\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 47ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 19ms/step\n",
      "Fold 7:\n",
      "  RMSEc: 10.8740\n",
      "  RMSEp: 30.2241\n",
      "  Rcal: 0.9744\n",
      "  Rval: 0.8059\n",
      "  RPD: 1.6629\n",
      "\n",
      "Fold 8\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 39ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 20ms/step\n",
      "Fold 8:\n",
      "  RMSEc: 10.6263\n",
      "  RMSEp: 13.8866\n",
      "  Rcal: 0.9767\n",
      "  Rval: 0.9494\n",
      "  RPD: 3.1185\n",
      "\n",
      "Fold 9\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m3s\u001b[0m 121ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 27ms/step\n",
      "Fold 9:\n",
      "  RMSEc: 6.9260\n",
      "  RMSEp: 7.5959\n",
      "  Rcal: 0.9895\n",
      "  Rval: 0.9907\n",
      "  RPD: 7.3482\n",
      "\n",
      "Fold 10\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 34ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 19ms/step\n",
      "Fold 10:\n",
      "  RMSEc: 5.2521\n",
      "  RMSEp: 8.9712\n",
      "  Rcal: 0.9942\n",
      "  Rval: 0.9824\n",
      "  RPD: 5.3313\n",
      "\n",
      "Cross-Validation Results:\n",
      "Average RMSEc: 7.5909 ± 2.5621\n",
      "Average RMSEp: 10.6474 ± 6.9927\n",
      "Average Rcal: 0.9866 ± 0.0080\n",
      "Average Rval: 0.9638 ± 0.0543\n",
      "Average RPD: 5.6206 ± 2.0844\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from sklearn.model_selection import KFold\n",
    "\n",
    "# 读取数据（保持不变）\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷A']]\n",
    "\n",
    "# 特征提取（保持不变）\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# 构建LSTM模型（保持不变）\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(256, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(LSTM(256))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=0.005), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "X = np.expand_dims(pca_features_df.values, axis=2)\n",
    "y = targets.values\n",
    "\n",
    "# 修改点：将n_splits参数从5改为10\n",
    "kf = KFold(n_splits=10, shuffle=True, random_state=42)  # 现在使用10折交叉验证\n",
    "\n",
    "# 性能指标存储（保持不变）\n",
    "rmsec_scores = []  \n",
    "rmsep_scores = []  \n",
    "rcal_scores = []   \n",
    "rval_scores = []   \n",
    "rpd_scores = []    \n",
    "\n",
    "# 交叉验证循环（自动适应10折）\n",
    "for fold, (train_indices, val_indices) in enumerate(kf.split(X)):\n",
    "    print(f\"\\nFold {fold + 1}\")\n",
    "    \n",
    "    X_train, X_val = X[train_indices], X[val_indices]\n",
    "    y_train, y_val = y[train_indices], y[val_indices]\n",
    "    \n",
    "    # 调整数据形状（保持不变）\n",
    "    X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])\n",
    "    X_val = X_val.reshape(X_val.shape[0], 1, X_val.shape[1])\n",
    "    \n",
    "    print(\"X_train shape:\", X_train.shape)\n",
    "    print(\"X_val shape:\", X_val.shape)\n",
    "    \n",
    "    model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "    \n",
    "    # 训练参数（保持不变）\n",
    "    early_stopping = EarlyStopping(monitor='val_loss', patience=100, restore_best_weights=True)\n",
    "    history = model_lstm.fit(\n",
    "        X_train, y_train,\n",
    "        epochs=1000,\n",
    "        batch_size=10,\n",
    "        validation_data=(X_val, y_val),\n",
    "        verbose=0,\n",
    "        callbacks=[early_stopping]\n",
    "    )\n",
    "    \n",
    "    # 测试和指标计算（保持不变）\n",
    "    y_pred_train = model_lstm.predict(X_train)\n",
    "    y_pred_val = model_lstm.predict(X_val)\n",
    "    \n",
    "    def calculate_metrics(y_true, y_pred):\n",
    "        rmse = np.sqrt(mean_squared_error(y_true, y_pred))\n",
    "        r = pearsonr(y_true.ravel(), y_pred.ravel())[0]\n",
    "        return rmse, r\n",
    "    \n",
    "    rmsec, rcal = calculate_metrics(y_train, y_pred_train)\n",
    "    rmsec_scores.append(rmsec)\n",
    "    rcal_scores.append(rcal)\n",
    "    \n",
    "    rmsep, rval = calculate_metrics(y_val, y_pred_val)\n",
    "    rmsep_scores.append(rmsep)\n",
    "    rval_scores.append(rval)\n",
    "    \n",
    "    rpd = np.std(y_val) / rmsep\n",
    "    rpd_scores.append(rpd)\n",
    "    \n",
    "    print(f\"Fold {fold + 1}:\")\n",
    "    print(f\"  RMSEc: {rmsec:.4f}\")\n",
    "    print(f\"  RMSEp: {rmsep:.4f}\")\n",
    "    print(f\"  Rcal: {rcal:.4f}\")\n",
    "    print(f\"  Rval: {rval:.4f}\")\n",
    "    print(f\"  RPD: {rpd:.4f}\")\n",
    "\n",
    "# 结果输出（保持不变，但会显示10折的结果）\n",
    "print(\"\\nCross-Validation Results:\")\n",
    "print(f\"Average RMSEc: {np.mean(rmsec_scores):.4f} ± {np.std(rmsec_scores):.4f}\")\n",
    "print(f\"Average RMSEp: {np.mean(rmsep_scores):.4f} ± {np.std(rmsep_scores):.4f}\")\n",
    "print(f\"Average Rcal: {np.mean(rcal_scores):.4f} ± {np.std(rcal_scores):.4f}\")\n",
    "print(f\"Average Rval: {np.mean(rval_scores):.4f} ± {np.std(rval_scores):.4f}\")\n",
    "print(f\"Average RPD: {np.mean(rpd_scores):.4f} ± {np.std(rpd_scores):.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d800c0b9-42c0-4865-aa27-a838ed8c0724",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Fold 1\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 41ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 24ms/step\n",
      "Fold 1:\n",
      "  RMSEc (校正均方根误差): 10.4644\n",
      "  RMSEp (预测均方根误差): 13.8581\n",
      "  Rcal (校正集相关系数): 0.9889\n",
      "  Rval (验证集相关系数): 0.9817\n",
      "  RPD (相对预测偏差): 5.2054\n",
      "\n",
      "Fold 2\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 35ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 22ms/step\n",
      "Fold 2:\n",
      "  RMSEc (校正均方根误差): 4.0766\n",
      "  RMSEp (预测均方根误差): 7.6349\n",
      "  Rcal (校正集相关系数): 0.9983\n",
      "  Rval (验证集相关系数): 0.9954\n",
      "  RPD (相对预测偏差): 9.6328\n",
      "\n",
      "Fold 3\n",
      "X_train shape: (362, 1, 10)\n",
      "X_val shape: (41, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 53ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 24ms/step\n",
      "Fold 3:\n",
      "  RMSEc (校正均方根误差): 8.8123\n",
      "  RMSEp (预测均方根误差): 13.9414\n",
      "  Rcal (校正集相关系数): 0.9916\n",
      "  Rval (验证集相关系数): 0.9749\n",
      "  RPD (相对预测偏差): 4.3931\n",
      "\n",
      "Fold 4\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 38ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 22ms/step\n",
      "Fold 4:\n",
      "  RMSEc (校正均方根误差): 4.2767\n",
      "  RMSEp (预测均方根误差): 6.0283\n",
      "  Rcal (校正集相关系数): 0.9981\n",
      "  Rval (验证集相关系数): 0.9948\n",
      "  RPD (相对预测偏差): 9.8254\n",
      "\n",
      "Fold 5\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m8s\u001b[0m 522ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 39ms/step\n",
      "Fold 5:\n",
      "  RMSEc (校正均方根误差): 2.5130\n",
      "  RMSEp (预测均方根误差): 11.2297\n",
      "  Rcal (校正集相关系数): 0.9994\n",
      "  Rval (验证集相关系数): 0.9830\n",
      "  RPD (相对预测偏差): 5.3194\n",
      "\n",
      "Fold 6\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m5s\u001b[0m 246ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 209ms/step\n",
      "Fold 6:\n",
      "  RMSEc (校正均方根误差): 4.0037\n",
      "  RMSEp (预测均方根误差): 8.9851\n",
      "  Rcal (校正集相关系数): 0.9983\n",
      "  Rval (验证集相关系数): 0.9817\n",
      "  RPD (相对预测偏差): 5.2181\n",
      "\n",
      "Fold 7\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 44ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 32ms/step\n",
      "Fold 7:\n",
      "  RMSEc (校正均方根误差): 8.8229\n",
      "  RMSEp (预测均方根误差): 31.4986\n",
      "  Rcal (校正集相关系数): 0.9913\n",
      "  Rval (验证集相关系数): 0.9076\n",
      "  RPD (相对预测偏差): 2.3233\n",
      "\n",
      "Fold 8\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m2s\u001b[0m 171ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 23ms/step\n",
      "Fold 8:\n",
      "  RMSEc (校正均方根误差): 1.9486\n",
      "  RMSEp (预测均方根误差): 11.3175\n",
      "  Rcal (校正集相关系数): 0.9996\n",
      "  Rval (验证集相关系数): 0.9861\n",
      "  RPD (相对预测偏差): 5.9647\n",
      "\n",
      "Fold 9\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m1s\u001b[0m 56ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 26ms/step\n",
      "Fold 9:\n",
      "  RMSEc (校正均方根误差): 8.5402\n",
      "  RMSEp (预测均方根误差): 11.4172\n",
      "  Rcal (校正集相关系数): 0.9919\n",
      "  Rval (验证集相关系数): 0.9884\n",
      "  RPD (相对预测偏差): 6.5665\n",
      "\n",
      "Fold 10\n",
      "X_train shape: (363, 1, 10)\n",
      "X_val shape: (40, 1, 10)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\Lib\\site-packages\\keras\\src\\layers\\rnn\\rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(**kwargs)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m12/12\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m3s\u001b[0m 161ms/step\n",
      "\u001b[1m2/2\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 28ms/step\n",
      "Fold 10:\n",
      "  RMSEc (校正均方根误差): 5.8984\n",
      "  RMSEp (预测均方根误差): 8.1381\n",
      "  Rcal (校正集相关系数): 0.9961\n",
      "  Rval (验证集相关系数): 0.9940\n",
      "  RPD (相对预测偏差): 9.1079\n",
      "\n",
      "Cross-Validation Results:\n",
      "Average RMSEc: 5.9357 ± 2.8541\n",
      "Average RMSEp: 12.4049 ± 6.8257\n",
      "Average Rcal: 0.9953 ± 0.0038\n",
      "Average Rval: 0.9788 ± 0.0245\n",
      "Average RPD: 6.3557 ± 2.3314\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from scipy.stats import pearsonr\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "from tensorflow.keras.layers import Dense, LSTM\n",
    "from sklearn.model_selection import KFold\n",
    "\n",
    "# 读取数据\n",
    "file_path = 'F:\\\\研究\\\\番泻苷在线提取数据.xlsx'\n",
    "with pd.ExcelFile(file_path) as xls:\n",
    "    ir_data = pd.read_excel(xls, '红外谱图', index_col='编号\\波数')\n",
    "    targets = pd.read_excel(xls, '番泻苷含量')[['番泻苷B']]\n",
    "\n",
    "# 特征提取\n",
    "def extract_features(data):\n",
    "    pca = PCA(n_components=10)\n",
    "    pca_features = pca.fit_transform(data)\n",
    "    return pd.DataFrame(pca_features, columns=['PC' + str(i) for i in range(1, 11)])\n",
    "\n",
    "pca_features_df = extract_features(ir_data)\n",
    "\n",
    "# 索引对齐\n",
    "pca_features_df, targets = pca_features_df.align(targets, join='inner', axis=0)\n",
    "\n",
    "# 构建LSTM模型\n",
    "def LSTM_model(input_shape, output_shape):\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(128, input_shape=input_shape, return_sequences=True))\n",
    "    model.add(LSTM(128))\n",
    "    model.add(Dense(output_shape))\n",
    "    model.compile(optimizer=Adam(learning_rate=1e-3), loss='mean_squared_error')\n",
    "    return model\n",
    "\n",
    "# 调整数据形状\n",
    "X = np.expand_dims(pca_features_df.values, axis=2)  # (samples, features, 1)\n",
    "y = targets.values\n",
    "\n",
    "# 定义交叉验证\n",
    "kf = KFold(n_splits=10, shuffle=True, random_state=42)  # 使用 5 折交叉验证\n",
    "\n",
    "# 性能指标存储\n",
    "rmsec_scores = []  # 校正均方根误差\n",
    "rmsep_scores = []  # 预测均方根误差\n",
    "rcal_scores = []   # 校正集相关系数\n",
    "rval_scores = []   # 验证集相关系数\n",
    "rpd_scores = []    # 相对预测偏差\n",
    "\n",
    "# 开始交叉验证\n",
    "for fold, (train_indices, val_indices) in enumerate(kf.split(X)):\n",
    "    print(f\"\\nFold {fold + 1}\")\n",
    "    \n",
    "    # 划分训练集和验证集\n",
    "    X_train, X_val = X[train_indices], X[val_indices]\n",
    "    y_train, y_val = y[train_indices], y[val_indices]\n",
    "    \n",
    "    # 调整数据形状以匹配LSTM输入要求\n",
    "    X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])  # (samples, timesteps, features)\n",
    "    X_val = X_val.reshape(X_val.shape[0], 1, X_val.shape[1])          # (samples, timesteps, features)\n",
    "    \n",
    "    # 检查数据形状\n",
    "    print(\"X_train shape:\", X_train.shape)\n",
    "    print(\"X_val shape:\", X_val.shape)\n",
    "    \n",
    "    # 创建LSTM模型\n",
    "    model_lstm = LSTM_model(X_train.shape[1:], 1)\n",
    "    \n",
    "    # 训练模型（使用早停机制）\n",
    "    early_stopping = EarlyStopping(monitor='val_loss', patience=100, restore_best_weights=True)\n",
    "    history = model_lstm.fit(\n",
    "        X_train, y_train,\n",
    "        epochs=1000,\n",
    "        batch_size=10,\n",
    "        validation_data=(X_val, y_val),\n",
    "        verbose=0,\n",
    "        callbacks=[early_stopping]\n",
    "    )\n",
    "    \n",
    "    # 测试模型\n",
    "    y_pred_train = model_lstm.predict(X_train)\n",
    "    y_pred_val = model_lstm.predict(X_val)\n",
    "    \n",
    "    # 计算性能指标\n",
    "    def calculate_metrics(y_true, y_pred):\n",
    "        rmse = np.sqrt(mean_squared_error(y_true, y_pred))  # RMSE\n",
    "        r = pearsonr(y_true.ravel(), y_pred.ravel())[0]     # 相关系数 R\n",
    "        return rmse, r\n",
    "    \n",
    "    # 校正集指标\n",
    "    rmsec, rcal = calculate_metrics(y_train, y_pred_train)\n",
    "    rmsec_scores.append(rmsec)\n",
    "    rcal_scores.append(rcal)\n",
    "    \n",
    "    # 验证集指标\n",
    "    rmsep, rval = calculate_metrics(y_val, y_pred_val)\n",
    "    rmsep_scores.append(rmsep)\n",
    "    rval_scores.append(rval)\n",
    "    \n",
    "    # RPD（相对预测偏差）\n",
    "    rpd = np.std(y_val) / rmsep\n",
    "    rpd_scores.append(rpd)\n",
    "    \n",
    "    # 打印当前折的指标\n",
    "    print(f\"Fold {fold + 1}:\")\n",
    "    print(f\"  RMSEc (校正均方根误差): {rmsec:.4f}\")\n",
    "    print(f\"  RMSEp (预测均方根误差): {rmsep:.4f}\")\n",
    "    print(f\"  Rcal (校正集相关系数): {rcal:.4f}\")\n",
    "    print(f\"  Rval (验证集相关系数): {rval:.4f}\")\n",
    "    print(f\"  RPD (相对预测偏差): {rpd:.4f}\")\n",
    "\n",
    "# 输出交叉验证结果\n",
    "print(\"\\nCross-Validation Results:\")\n",
    "print(f\"Average RMSEc: {np.mean(rmsec_scores):.4f} ± {np.std(rmsec_scores):.4f}\")\n",
    "print(f\"Average RMSEp: {np.mean(rmsep_scores):.4f} ± {np.std(rmsep_scores):.4f}\")\n",
    "print(f\"Average Rcal: {np.mean(rcal_scores):.4f} ± {np.std(rcal_scores):.4f}\")\n",
    "print(f\"Average Rval: {np.mean(rval_scores):.4f} ± {np.std(rval_scores):.4f}\")\n",
    "print(f\"Average RPD: {np.mean(rpd_scores):.4f} ± {np.std(rpd_scores):.4f}\")\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
